blob: 9b0b960cb1c57a1817bdda051ed8c388ac599f49 [file] [log] [blame]
/*
* This file defines the writing rr graph function in XML format.
* The rr graph is separated into channels, nodes, switches,
* grids, edges, block types, and segments. Each tag has several
* children tags such as timing, location, or some general
* details. Each tag has attributes to describe them */
#include <fstream>
#include <iostream>
#include <string.h>
#include <iomanip>
#include <limits>
#include "vpr_error.h"
#include "globals.h"
#include "read_xml_arch_file.h"
#include "vtr_version.h"
#include "rr_graph_writer.h"
/* All values are printed with this precision value. The higher the
* value, the more accurate the read in rr graph is. Using numeric_limits
* max_digits10 guarentees that no values change during a sequence of
* float -> string -> float conversions */
constexpr int FLOAT_PRECISION = std::numeric_limits<float>::max_digits10;
/*********************** Subroutines local to this module *******************/
void write_rr_channel(std::fstream& fp);
void write_rr_node(std::fstream& fp);
void write_rr_switches(std::fstream& fp);
void write_rr_grid(std::fstream& fp);
void write_rr_edges(std::fstream& fp);
void write_rr_block_types(std::fstream& fp);
void write_rr_segments(std::fstream& fp, const std::vector<t_segment_inf>& segment_inf);
/************************ Subroutine definitions ****************************/
/* This function is used to write the rr_graph into xml format into a a file with name: file_name */
void write_rr_graph(const char* file_name, const std::vector<t_segment_inf>& segment_inf) {
std::fstream fp;
fp.open(file_name, std::fstream::out | std::fstream::trunc);
/* Prints out general info for easy error checking*/
if (!fp.is_open() || !fp.good()) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"couldn't open file \"%s\" for generating RR graph file\n", file_name);
}
std::cout << "Writing RR graph" << std::endl;
fp << "<rr_graph tool_name=\"vpr\" tool_version=\"" << vtr::VERSION << "\" tool_comment=\"Generated from arch file "
<< get_arch_file_name() << "\">" << std::endl;
/* Write out each individual component*/
write_rr_channel(fp);
write_rr_switches(fp);
write_rr_segments(fp, segment_inf);
write_rr_block_types(fp);
write_rr_grid(fp);
write_rr_node(fp);
write_rr_edges(fp);
fp << "</rr_graph>";
fp.close();
std::cout << "Finished generating RR graph file named " << file_name << std::endl
<< std::endl;
}
static void add_metadata_to_xml(std::fstream& fp, const char* tab_prefix, const t_metadata_dict& meta) {
fp << tab_prefix << "<metadata>" << std::endl;
for (const auto& meta_elem : meta) {
const std::string& key = meta_elem.first;
const std::vector<t_metadata_value>& values = meta_elem.second;
for (const auto& value : values) {
fp << tab_prefix << "\t<meta name=\"" << key << "\"";
fp << ">" << value.as_string() << "</meta>" << std::endl;
}
}
fp << tab_prefix << "</metadata>" << std::endl;
}
/* Channel info in device_ctx.chan_width is written in xml format.
* A general summary of the min and max values of the channels are first printed. Every
* x and y channel list is printed out in its own attribute*/
void write_rr_channel(std::fstream& fp) {
auto& device_ctx = g_vpr_ctx.device();
fp << "\t<channels>" << std::endl;
fp << "\t\t<channel chan_width_max =\"" << device_ctx.chan_width.max << "\" x_min=\"" << device_ctx.chan_width.x_min << "\" y_min=\"" << device_ctx.chan_width.y_min << "\" x_max=\"" << device_ctx.chan_width.x_max << "\" y_max=\"" << device_ctx.chan_width.y_max << "\"/>" << std::endl;
auto& list = device_ctx.chan_width.x_list;
for (size_t i = 0; i < device_ctx.grid.height() - 1; i++) {
fp << "\t\t<x_list index =\"" << i << "\" info=\"" << list[i] << "\"/>" << std::endl;
}
auto& list2 = device_ctx.chan_width.y_list;
for (size_t i = 0; i < device_ctx.grid.width() - 1; i++) {
fp << "\t\t<y_list index =\"" << i << "\" info=\"" << list2[i] << "\"/>" << std::endl;
}
fp << "\t</channels>" << std::endl;
}
/* All relevant rr node info is written out to the graph.
* This includes location, timing, and segment info*/
void write_rr_node(std::fstream& fp) {
auto& device_ctx = g_vpr_ctx.device();
fp << "\t<rr_nodes>" << std::endl;
for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); inode++) {
auto& node = device_ctx.rr_nodes[inode];
fp << "\t\t<node";
fp << " id=\"" << inode;
fp << "\" type=\"" << node.type_string();
if (node.type() == CHANX || node.type() == CHANY) {
fp << "\" direction=\"" << node.direction_string();
}
fp << "\" capacity=\"" << node.capacity();
fp << "\">" << std::endl;
fp << "\t\t\t<loc";
fp << " xlow=\"" << node.xlow();
fp << "\" ylow=\"" << node.ylow();
fp << "\" xhigh=\"" << node.xhigh();
fp << "\" yhigh=\"" << node.yhigh();
if (node.type() == IPIN || node.type() == OPIN) {
fp << "\" side=\"" << node.side_string();
}
fp << "\" ptc=\"" << node.ptc_num();
fp << "\"/>" << std::endl;
fp << "\t\t\t<timing R=\"" << std::setprecision(FLOAT_PRECISION) << node.R()
<< "\" C=\"" << std::setprecision(FLOAT_PRECISION) << node.C() << "\"/>" << std::endl;
if (device_ctx.rr_indexed_data[node.cost_index()].seg_index != -1) {
fp << "\t\t\t<segment segment_id=\"" << device_ctx.rr_indexed_data[node.cost_index()].seg_index << "\"/>" << std::endl;
}
const auto iter = device_ctx.rr_node_metadata.find(inode);
if (iter != device_ctx.rr_node_metadata.end()) {
const t_metadata_dict& meta = iter->second;
add_metadata_to_xml(fp, "\t\t\t", meta);
}
fp << "\t\t</node>" << std::endl;
}
fp << "\t</rr_nodes>" << std::endl
<< std::endl;
}
/* Segment information in the t_segment_inf data structure is written out.
* Information includes segment id, name, and optional timing parameters*/
void write_rr_segments(std::fstream& fp, const std::vector<t_segment_inf>& segment_inf) {
fp << "\t<segments>" << std::endl;
for (size_t iseg = 0; iseg < segment_inf.size(); iseg++) {
fp << "\t\t<segment id=\"" << iseg << "\" name=\"" << segment_inf[iseg].name << "\">" << std::endl;
fp << "\t\t\t<timing R_per_meter=\"" << std::setprecision(FLOAT_PRECISION) << segment_inf[iseg].Rmetal << "\" C_per_meter=\"" << std::setprecision(FLOAT_PRECISION) << segment_inf[iseg].Cmetal << "\"/>" << std::endl;
fp << "\t\t</segment>" << std::endl;
}
fp << "\t</segments>" << std::endl
<< std::endl;
}
/* Switch info is written out into xml format. This includes
* general, sizing, and optional timing information*/
void write_rr_switches(std::fstream& fp) {
auto& device_ctx = g_vpr_ctx.device();
fp << "\t<switches>" << std::endl;
for (size_t iSwitch = 0; iSwitch < device_ctx.rr_switch_inf.size(); iSwitch++) {
t_rr_switch_inf rr_switch = device_ctx.rr_switch_inf[iSwitch];
fp << "\t\t<switch id=\"" << iSwitch;
if (rr_switch.type() == SwitchType::TRISTATE) {
fp << "\" type=\"tristate";
} else if (rr_switch.type() == SwitchType::MUX) {
fp << "\" type=\"mux";
} else if (rr_switch.type() == SwitchType::PASS_GATE) {
fp << "\" type=\"pass_gate";
} else if (rr_switch.type() == SwitchType::SHORT) {
fp << "\" type=\"short";
} else if (rr_switch.type() == SwitchType::BUFFER) {
fp << "\" type=\"buffer";
} else {
VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Invalid switch type %d\n", rr_switch.type());
}
fp << "\"";
if (rr_switch.name) {
fp << " name=\"" << rr_switch.name << "\"";
}
fp << ">" << std::endl;
fp << "\t\t\t<timing R=\"" << std::setprecision(FLOAT_PRECISION) << rr_switch.R << "\" Cin=\"" << std::setprecision(FLOAT_PRECISION) << rr_switch.Cin << "\" Cout=\"" << std::setprecision(FLOAT_PRECISION) << rr_switch.Cout << "\" Cinternal=\"" << std::setprecision(FLOAT_PRECISION) << rr_switch.Cinternal << "\" Tdel=\"" << std::setprecision(FLOAT_PRECISION) << rr_switch.Tdel << "\"/>" << std::endl;
fp << "\t\t\t<sizing mux_trans_size=\"" << std::setprecision(FLOAT_PRECISION) << rr_switch.mux_trans_size << "\" buf_size=\"" << std::setprecision(FLOAT_PRECISION) << rr_switch.buf_size << "\"/>" << std::endl;
fp << "\t\t</switch>" << std::endl;
}
fp << "\t</switches>" << std::endl
<< std::endl;
}
/* Block information is printed out in xml format. This includes general,
* pin class, and pins */
void write_rr_block_types(std::fstream& fp) {
auto& device_ctx = g_vpr_ctx.device();
fp << "\t<block_types>" << std::endl;
for (const auto& btype : device_ctx.physical_tile_types) {
fp << "\t\t<block_type id=\"" << btype.index;
VTR_ASSERT(btype.name);
fp << "\" name=\"" << btype.name;
fp << "\" width=\"" << btype.width << "\" height=\"" << btype.height << "\">" << std::endl;
for (int iClass = 0; iClass < btype.num_class; iClass++) {
auto& class_inf = btype.class_inf[iClass];
char const* pin_type;
switch (class_inf.type) {
case -1:
pin_type = "OPEN";
break;
case 0:
pin_type = "OUTPUT"; //driver
break;
case 1:
pin_type = "INPUT"; //receiver
break;
default:
pin_type = "NONE";
break;
}
//the pin list is printed out as the child values
fp << "\t\t\t<pin_class type=\"" << pin_type << "\">\n";
for (int iPin = 0; iPin < class_inf.num_pins; iPin++) {
auto pin = class_inf.pinlist[iPin];
fp << vtr::string_fmt("\t\t\t\t<pin ptc=\"%d\">%s</pin>\n",
pin, block_type_pin_index_to_name(&btype, pin).c_str());
}
fp << "\t\t\t</pin_class>" << std::endl;
}
fp << "\t\t</block_type>" << std::endl;
}
fp << "\t</block_types>" << std::endl
<< std::endl;
}
/* Grid information is printed out in xml format. Each grid location
* and its relevant information is included*/
void write_rr_grid(std::fstream& fp) {
auto& device_ctx = g_vpr_ctx.device();
fp << "\t<grid>" << std::endl;
for (size_t x = 0; x < device_ctx.grid.width(); x++) {
for (size_t y = 0; y < device_ctx.grid.height(); y++) {
t_grid_tile grid_tile = device_ctx.grid[x][y];
fp << "\t\t<grid_loc x=\"" << x << "\" y=\"" << y << "\" block_type_id=\"" << grid_tile.type->index << "\" width_offset=\"" << grid_tile.width_offset << "\" height_offset=\"" << grid_tile.height_offset << "\"/>" << std::endl;
}
}
fp << "\t</grid>" << std::endl
<< std::endl;
}
/* Edges connecting to each rr node is printed out. The two nodes
* it connects to are also printed*/
void write_rr_edges(std::fstream& fp) {
auto& device_ctx = g_vpr_ctx.device();
fp << "\t<rr_edges>" << std::endl;
for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); inode++) {
auto& node = device_ctx.rr_nodes[inode];
for (t_edge_size iedge = 0; iedge < node.num_edges(); iedge++) {
fp << "\t\t<edge src_node=\"" << inode << "\" sink_node=\"" << node.edge_sink_node(iedge) << "\" switch_id=\"" << node.edge_switch(iedge) << "\"";
bool wrote_edge_metadata = false;
const auto iter = device_ctx.rr_edge_metadata.find(std::make_tuple(inode, node.edge_sink_node(iedge), node.edge_switch(iedge)));
if (iter != device_ctx.rr_edge_metadata.end()) {
fp << ">" << std::endl;
const t_metadata_dict& meta = iter->second;
add_metadata_to_xml(fp, "\t\t\t", meta);
wrote_edge_metadata = true;
}
if (wrote_edge_metadata == false) {
fp << "/>" << std::endl;
} else {
fp << "\t\t</edge>" << std::endl;
}
}
}
fp << "\t</rr_edges>" << std::endl
<< std::endl;
}