448 lines
19 KiB
C++
448 lines
19 KiB
C++
/***************************************************************************************
|
|
* This file includes functions that are used to redo packing for physical pbs
|
|
***************************************************************************************/
|
|
|
|
/* Headers from vtrutil library */
|
|
#include "build_physical_lb_rr_graph.h"
|
|
|
|
#include "check_lb_rr_graph.h"
|
|
#include "pb_type_utils.h"
|
|
#include "vtr_assert.h"
|
|
#include "vtr_log.h"
|
|
#include "vtr_time.h"
|
|
|
|
/* begin namespace openfpga */
|
|
namespace openfpga {
|
|
|
|
/***************************************************************************************
|
|
* Create all the intermediate nodes for lb_rr_graph for each pb_graph_node.
|
|
* Different from the lb_rr_graph builder in VPR packer, this function only
|
|
*consider the pb_graph_node under physical modes
|
|
***************************************************************************************/
|
|
static void rec_build_physical_lb_rr_node_for_pb_graph_node(
|
|
t_pb_graph_node* pb_graph_node, LbRRGraph& lb_rr_graph,
|
|
const VprDeviceAnnotation& device_annotation) {
|
|
t_pb_type* pb_type = pb_graph_node->pb_type;
|
|
|
|
/* TODO: think if we need to consider wire mode of LUT when creating the
|
|
* lb_rr_graph here! Should we create edges through the LUT input and output
|
|
* nodes?
|
|
*/
|
|
|
|
/* The only difference between primitive node and intermediate nodes is
|
|
* the output pins of primitive node will be SINK node
|
|
* Otherwise it is always INTERMEDIATE node
|
|
*/
|
|
e_lb_rr_type output_pin_rr_type = LB_INTERMEDIATE;
|
|
if (true == is_primitive_pb_type(pb_type)) {
|
|
output_pin_rr_type = LB_SOURCE;
|
|
}
|
|
|
|
/* alloc and load input pins that connect to sinks */
|
|
for (int iport = 0; iport < pb_graph_node->num_input_ports; iport++) {
|
|
for (int ipin = 0; ipin < pb_graph_node->num_input_pins[iport]; ipin++) {
|
|
/* load intermediate indices */
|
|
t_pb_graph_pin* pb_pin = &pb_graph_node->input_pins[iport][ipin];
|
|
|
|
/* alloc and load rr node info */
|
|
LbRRNodeId node = lb_rr_graph.create_node(LB_INTERMEDIATE);
|
|
lb_rr_graph.set_node_capacity(node, 1);
|
|
lb_rr_graph.set_node_pb_graph_pin(node, pb_pin);
|
|
|
|
/* TODO: Double check if this is the case */
|
|
lb_rr_graph.set_node_intrinsic_cost(node, 1);
|
|
}
|
|
}
|
|
|
|
/* alloc and load input pins that connect to sinks */
|
|
for (int iport = 0; iport < pb_graph_node->num_clock_ports; iport++) {
|
|
for (int ipin = 0; ipin < pb_graph_node->num_clock_pins[iport]; ipin++) {
|
|
/* load intermediate indices */
|
|
t_pb_graph_pin* pb_pin = &pb_graph_node->clock_pins[iport][ipin];
|
|
|
|
/* alloc and load rr node info */
|
|
LbRRNodeId node = lb_rr_graph.create_node(LB_INTERMEDIATE);
|
|
lb_rr_graph.set_node_capacity(node, 1);
|
|
lb_rr_graph.set_node_pb_graph_pin(node, pb_pin);
|
|
|
|
/* TODO: Double check if this is the case */
|
|
lb_rr_graph.set_node_intrinsic_cost(node, 1);
|
|
}
|
|
}
|
|
|
|
/* alloc and load output pins that are represented as rr sources */
|
|
for (int iport = 0; iport < pb_graph_node->num_output_ports; iport++) {
|
|
for (int ipin = 0; ipin < pb_graph_node->num_output_pins[iport]; ipin++) {
|
|
/* load intermediate indices */
|
|
t_pb_graph_pin* pb_pin = &pb_graph_node->output_pins[iport][ipin];
|
|
|
|
/* alloc and load rr node info */
|
|
LbRRNodeId node = lb_rr_graph.create_node(output_pin_rr_type);
|
|
lb_rr_graph.set_node_capacity(node, 1);
|
|
lb_rr_graph.set_node_pb_graph_pin(node, pb_pin);
|
|
|
|
/* TODO: Double check if this is the case */
|
|
lb_rr_graph.set_node_intrinsic_cost(node, 1);
|
|
}
|
|
}
|
|
|
|
if (true == is_primitive_pb_type(pb_type)) {
|
|
return;
|
|
}
|
|
|
|
/* For non-primitive node:
|
|
* This pb_graph_node is a logic block or subcluster
|
|
* Go recusrively
|
|
*/
|
|
t_mode* physical_mode = device_annotation.physical_mode(pb_type);
|
|
for (int ipb_type = 0; ipb_type < physical_mode->num_pb_type_children;
|
|
ipb_type++) {
|
|
for (int ipb = 0; ipb < physical_mode->pb_type_children[ipb_type].num_pb;
|
|
ipb++) {
|
|
rec_build_physical_lb_rr_node_for_pb_graph_node(
|
|
&(pb_graph_node
|
|
->child_pb_graph_nodes[physical_mode->index][ipb_type][ipb]),
|
|
lb_rr_graph, device_annotation);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************************
|
|
* Build the edge for an input/clock pb_graph_pin for a primitive pb_graph node
|
|
* This function will identify if the port equivalence should be considered
|
|
***************************************************************************************/
|
|
static void build_lb_rr_edge_primitive_pb_graph_input_pin(
|
|
LbRRGraph& lb_rr_graph, t_pb_graph_pin* pb_pin, LbRRNodeId& sink_node) {
|
|
/* Find the node that we have already created */
|
|
LbRRNodeId node = lb_rr_graph.find_node(LB_INTERMEDIATE, pb_pin);
|
|
VTR_ASSERT(true == lb_rr_graph.valid_node_id(node));
|
|
|
|
PortEquivalence port_equivalent = pb_pin->port->equivalent;
|
|
|
|
if (port_equivalent == PortEquivalence::NONE ||
|
|
sink_node == LbRRNodeId::INVALID()) {
|
|
/* Create new sink for input to primitive */
|
|
LbRRNodeId new_sink = lb_rr_graph.create_node(LB_SINK);
|
|
if (port_equivalent != PortEquivalence::NONE) {
|
|
lb_rr_graph.set_node_capacity(new_sink, pb_pin->port->num_pins);
|
|
} else {
|
|
lb_rr_graph.set_node_capacity(new_sink, 1);
|
|
}
|
|
sink_node = new_sink;
|
|
}
|
|
|
|
/* Connect the nodes denoting the input pins to sink, since this is a primtive
|
|
* node, we do not have any mode */
|
|
LbRREdgeId edge = lb_rr_graph.create_edge(node, sink_node, nullptr);
|
|
|
|
/* TODO: Double check if this is the case */
|
|
lb_rr_graph.set_edge_intrinsic_cost(edge, 1.);
|
|
}
|
|
|
|
/***************************************************************************************
|
|
* Build the edge for a pb_graph_pin for a non-primitive pb_graph node
|
|
* Note:
|
|
* - this function is NOT applicable to
|
|
* - any input pin of primitive pb_graph_node
|
|
* - any output pin of root pb_graph_node!
|
|
***************************************************************************************/
|
|
static void build_lb_rr_edge_pb_graph_pin(LbRRGraph& lb_rr_graph,
|
|
t_pb_graph_pin* pb_pin,
|
|
const e_lb_rr_type& pin_rr_type,
|
|
t_mode* physical_mode) {
|
|
/* Find the node that we have already created */
|
|
LbRRNodeId from_node = lb_rr_graph.find_node(pin_rr_type, pb_pin);
|
|
VTR_ASSERT(true == lb_rr_graph.valid_node_id(from_node));
|
|
|
|
/* Load edges only for physical mode! */
|
|
for (int iedge = 0; iedge < pb_pin->num_output_edges; iedge++) {
|
|
VTR_ASSERT(1 == pb_pin->output_edges[iedge]->num_output_pins);
|
|
if (physical_mode !=
|
|
pb_pin->output_edges[iedge]->interconnect->parent_mode) {
|
|
continue;
|
|
}
|
|
/* Find the node that we have already created */
|
|
LbRRNodeId to_node = lb_rr_graph.find_node(
|
|
LB_INTERMEDIATE, pb_pin->output_edges[iedge]->output_pins[0]);
|
|
VTR_ASSERT(true == lb_rr_graph.valid_node_id(to_node));
|
|
LbRREdgeId edge =
|
|
lb_rr_graph.create_edge(from_node, to_node, physical_mode);
|
|
|
|
/* TODO: Double check if this is the case */
|
|
lb_rr_graph.set_edge_intrinsic_cost(edge, 1.);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************************
|
|
* Build the edge for an output pb_graph_pin for a root pb_graph node
|
|
* These node should be connected to a command external lb_rr_node
|
|
***************************************************************************************/
|
|
static void build_lb_rr_edge_root_pb_graph_pin(LbRRGraph& lb_rr_graph,
|
|
t_pb_graph_pin* pb_pin,
|
|
const e_lb_rr_type& pin_rr_type,
|
|
const LbRRNodeId& ext_rr_index) {
|
|
/* Find the node that we have already created */
|
|
LbRRNodeId from_node = lb_rr_graph.find_node(pin_rr_type, pb_pin);
|
|
VTR_ASSERT(true == lb_rr_graph.valid_node_id(from_node));
|
|
|
|
LbRREdgeId edge = lb_rr_graph.create_edge(from_node, ext_rr_index, nullptr);
|
|
|
|
/* TODO: Double check if this is the case */
|
|
lb_rr_graph.set_edge_intrinsic_cost(edge, 1.);
|
|
}
|
|
|
|
/***************************************************************************************
|
|
* Create all the edges and special nodes (SOURCE/SINK) for lb_rr_graph for each
|
|
*pb_graph_node. Different from the lb_rr_graph builder in VPR packer, this
|
|
*function only consider the pb_graph_node under physical modes
|
|
***************************************************************************************/
|
|
static void rec_build_physical_lb_rr_edge_for_pb_graph_node(
|
|
t_pb_graph_node* pb_graph_node, LbRRGraph& lb_rr_graph,
|
|
const LbRRNodeId& ext_rr_index,
|
|
const VprDeviceAnnotation& device_annotation) {
|
|
t_pb_type* pb_type = pb_graph_node->pb_type;
|
|
t_pb_graph_node* parent_node = pb_graph_node->parent_pb_graph_node;
|
|
|
|
/* TODO: think if we need to consider wire mode of LUT when creating the
|
|
* lb_rr_graph here! Should we create edges through the LUT input and output
|
|
* nodes?
|
|
*/
|
|
|
|
/* The only difference between primitive node and intermediate nodes is
|
|
* the output pins of primitive node will be SINK node
|
|
* Otherwise it is always INTERMEDIATE node
|
|
*/
|
|
/* The input pins should connect to sinks */
|
|
for (int iport = 0; iport < pb_graph_node->num_input_ports; iport++) {
|
|
LbRRNodeId sink_node = LbRRNodeId::INVALID();
|
|
for (int ipin = 0; ipin < pb_graph_node->num_input_pins[iport]; ipin++) {
|
|
/* load intermediate indices */
|
|
t_pb_graph_pin* pb_pin = &pb_graph_node->input_pins[iport][ipin];
|
|
t_mode* physical_mode = device_annotation.physical_mode(pb_type);
|
|
|
|
if (true == is_primitive_pb_type(pb_type)) {
|
|
build_lb_rr_edge_primitive_pb_graph_input_pin(lb_rr_graph, pb_pin,
|
|
sink_node);
|
|
} else {
|
|
VTR_ASSERT(false == is_primitive_pb_type(pb_type));
|
|
build_lb_rr_edge_pb_graph_pin(lb_rr_graph, pb_pin, LB_INTERMEDIATE,
|
|
physical_mode);
|
|
}
|
|
}
|
|
}
|
|
/* The input pins should connect to sinks */
|
|
for (int iport = 0; iport < pb_graph_node->num_clock_ports; iport++) {
|
|
LbRRNodeId sink_node = LbRRNodeId::INVALID();
|
|
for (int ipin = 0; ipin < pb_graph_node->num_clock_pins[iport]; ipin++) {
|
|
/* load intermediate indices */
|
|
t_pb_graph_pin* pb_pin = &pb_graph_node->clock_pins[iport][ipin];
|
|
t_mode* physical_mode = device_annotation.physical_mode(pb_type);
|
|
|
|
if (true == is_primitive_pb_type(pb_type)) {
|
|
build_lb_rr_edge_primitive_pb_graph_input_pin(lb_rr_graph, pb_pin,
|
|
sink_node);
|
|
} else {
|
|
VTR_ASSERT(false == is_primitive_pb_type(pb_type));
|
|
build_lb_rr_edge_pb_graph_pin(lb_rr_graph, pb_pin, LB_INTERMEDIATE,
|
|
physical_mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
e_lb_rr_type output_pin_rr_type = LB_INTERMEDIATE;
|
|
if (true == is_primitive_pb_type(pb_type)) {
|
|
output_pin_rr_type = LB_SOURCE;
|
|
}
|
|
|
|
/* The output pins should connect to its fan-outs */
|
|
for (int iport = 0; iport < pb_graph_node->num_output_ports; iport++) {
|
|
for (int ipin = 0; ipin < pb_graph_node->num_output_pins[iport]; ipin++) {
|
|
/* load intermediate indices */
|
|
t_pb_graph_pin* pb_pin = &pb_graph_node->output_pins[iport][ipin];
|
|
|
|
if (true == pb_graph_node->is_root()) {
|
|
build_lb_rr_edge_root_pb_graph_pin(lb_rr_graph, pb_pin,
|
|
output_pin_rr_type, ext_rr_index);
|
|
} else {
|
|
VTR_ASSERT(false == pb_graph_node->is_root());
|
|
t_mode* physical_mode =
|
|
device_annotation.physical_mode(parent_node->pb_type);
|
|
build_lb_rr_edge_pb_graph_pin(lb_rr_graph, pb_pin, output_pin_rr_type,
|
|
physical_mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (true == is_primitive_pb_type(pb_type)) {
|
|
return;
|
|
}
|
|
|
|
/* For non-primitive node:
|
|
* This pb_graph_node is a logic block or subcluster
|
|
* Go recusrively
|
|
*/
|
|
t_mode* physical_mode =
|
|
device_annotation.physical_mode(pb_graph_node->pb_type);
|
|
for (int ipb_type = 0; ipb_type < physical_mode->num_pb_type_children;
|
|
ipb_type++) {
|
|
for (int ipb = 0; ipb < physical_mode->pb_type_children[ipb_type].num_pb;
|
|
ipb++) {
|
|
rec_build_physical_lb_rr_edge_for_pb_graph_node(
|
|
&(pb_graph_node
|
|
->child_pb_graph_nodes[physical_mode->index][ipb_type][ipb]),
|
|
lb_rr_graph, ext_rr_index, device_annotation);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************************
|
|
* This functio will create a physical lb_rr_graph for a pb_graph considering
|
|
*physical modes only
|
|
***************************************************************************************/
|
|
static LbRRGraph build_lb_type_physical_lb_rr_graph(
|
|
t_pb_graph_node* pb_graph_head, const VprDeviceAnnotation& device_annotation,
|
|
const bool& verbose) {
|
|
LbRRGraph lb_rr_graph;
|
|
|
|
/* TODO: ensure we have an empty lb_rr_graph */
|
|
|
|
/* Define the external source, sink, and external interconnect for the routing
|
|
* resource graph of the logic block type */
|
|
LbRRNodeId ext_source_index = lb_rr_graph.create_node(LB_SOURCE);
|
|
LbRRNodeId ext_sink_index = lb_rr_graph.create_node(LB_SINK);
|
|
LbRRNodeId ext_rr_index = lb_rr_graph.create_node(LB_INTERMEDIATE);
|
|
|
|
/* Build the main body of lb rr_graph by walking through the pb_graph
|
|
* recursively */
|
|
/* Build all the regular nodes first */
|
|
rec_build_physical_lb_rr_node_for_pb_graph_node(pb_graph_head, lb_rr_graph,
|
|
device_annotation);
|
|
/* Build all the edges and special node (SOURCE/SINK) */
|
|
rec_build_physical_lb_rr_edge_for_pb_graph_node(
|
|
pb_graph_head, lb_rr_graph, ext_rr_index, device_annotation);
|
|
|
|
/*******************************************************************************
|
|
* Build logic block source node
|
|
*******************************************************************************/
|
|
t_pb_type* pb_type = pb_graph_head->pb_type;
|
|
|
|
/* External source node drives all inputs going into logic block type */
|
|
lb_rr_graph.set_node_capacity(
|
|
ext_source_index, pb_type->num_input_pins + pb_type->num_clock_pins);
|
|
|
|
for (int iport = 0; iport < pb_graph_head->num_input_ports; iport++) {
|
|
for (int ipin = 0; ipin < pb_graph_head->num_input_pins[iport]; ipin++) {
|
|
LbRRNodeId to_node = lb_rr_graph.find_node(
|
|
LB_INTERMEDIATE, &(pb_graph_head->input_pins[iport][ipin]));
|
|
VTR_ASSERT(true == lb_rr_graph.valid_node_id(to_node));
|
|
LbRREdgeId edge =
|
|
lb_rr_graph.create_edge(ext_source_index, to_node, nullptr);
|
|
lb_rr_graph.set_edge_intrinsic_cost(edge, 1.);
|
|
}
|
|
}
|
|
|
|
for (int iport = 0; iport < pb_graph_head->num_clock_ports; iport++) {
|
|
for (int ipin = 0; ipin < pb_graph_head->num_clock_pins[iport]; ipin++) {
|
|
LbRRNodeId to_node = lb_rr_graph.find_node(
|
|
LB_INTERMEDIATE, &(pb_graph_head->clock_pins[iport][ipin]));
|
|
VTR_ASSERT(true == lb_rr_graph.valid_node_id(to_node));
|
|
LbRREdgeId edge =
|
|
lb_rr_graph.create_edge(ext_source_index, to_node, nullptr);
|
|
lb_rr_graph.set_edge_intrinsic_cost(edge, 1.);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Build logic block sink node
|
|
*******************************************************************************/
|
|
|
|
/* External sink node driven by all outputs exiting logic block type */
|
|
lb_rr_graph.set_node_capacity(ext_sink_index, pb_type->num_output_pins);
|
|
|
|
/*******************************************************************************
|
|
* Build node that approximates external interconnect
|
|
*******************************************************************************/
|
|
|
|
/* External rr node that drives all existing logic block input pins and is
|
|
* driven by all outputs exiting logic block type */
|
|
lb_rr_graph.set_node_capacity(ext_rr_index, pb_type->num_output_pins);
|
|
|
|
/* Connect opin of logic block to sink */
|
|
{
|
|
LbRREdgeId edge =
|
|
lb_rr_graph.create_edge(ext_rr_index, ext_sink_index, nullptr);
|
|
lb_rr_graph.set_edge_intrinsic_cost(edge, 1.);
|
|
}
|
|
|
|
/* Connect opin of logic block to all input and clock pins of logic block type
|
|
*/
|
|
for (int iport = 0; iport < pb_graph_head->num_input_ports; iport++) {
|
|
for (int ipin = 0; ipin < pb_graph_head->num_input_pins[iport]; ipin++) {
|
|
LbRRNodeId to_node = lb_rr_graph.find_node(
|
|
LB_INTERMEDIATE, &(pb_graph_head->input_pins[iport][ipin]));
|
|
VTR_ASSERT(true == lb_rr_graph.valid_node_id(to_node));
|
|
LbRREdgeId edge = lb_rr_graph.create_edge(ext_rr_index, to_node, nullptr);
|
|
/* set cost high to avoid using external interconnect unless necessary */
|
|
lb_rr_graph.set_edge_intrinsic_cost(edge, 1000.);
|
|
}
|
|
}
|
|
for (int iport = 0; iport < pb_graph_head->num_clock_ports; iport++) {
|
|
for (int ipin = 0; ipin < pb_graph_head->num_clock_pins[iport]; ipin++) {
|
|
LbRRNodeId to_node = lb_rr_graph.find_node(
|
|
LB_INTERMEDIATE, &(pb_graph_head->clock_pins[iport][ipin]));
|
|
VTR_ASSERT(true == lb_rr_graph.valid_node_id(to_node));
|
|
LbRREdgeId edge = lb_rr_graph.create_edge(ext_rr_index, to_node, nullptr);
|
|
/* set cost high to avoid using external interconnect unless necessary */
|
|
lb_rr_graph.set_edge_intrinsic_cost(edge, 1000.);
|
|
}
|
|
}
|
|
|
|
VTR_LOGV(verbose, "\n\tNumber of nodes: %lu\n", lb_rr_graph.nodes().size());
|
|
|
|
VTR_LOGV(verbose, "\tNumber of edges: %lu\n", lb_rr_graph.edges().size());
|
|
|
|
return lb_rr_graph;
|
|
}
|
|
|
|
/***************************************************************************************
|
|
* This functio will create physical lb_rr_graph for each pb_graph considering
|
|
*physical modes only the lb_rr_graph willbe added to device annotation
|
|
***************************************************************************************/
|
|
void build_physical_lb_rr_graphs(const DeviceContext& device_ctx,
|
|
VprDeviceAnnotation& device_annotation,
|
|
const bool& verbose) {
|
|
vtr::ScopedStartFinishTimer timer(
|
|
"Build routing resource graph for the physical implementation of logical "
|
|
"tile");
|
|
|
|
for (const t_logical_block_type& lb_type : device_ctx.logical_block_types) {
|
|
/* By pass nullptr for pb_graph head */
|
|
if (nullptr == lb_type.pb_graph_head) {
|
|
continue;
|
|
}
|
|
|
|
VTR_LOGV(verbose,
|
|
"Building routing resource graph for logical tile '%s'...",
|
|
lb_type.pb_graph_head->pb_type->name);
|
|
|
|
const LbRRGraph& lb_rr_graph = build_lb_type_physical_lb_rr_graph(
|
|
lb_type.pb_graph_head,
|
|
const_cast<const VprDeviceAnnotation&>(device_annotation), verbose);
|
|
/* Check the rr_graph */
|
|
if (false == lb_rr_graph.validate()) {
|
|
exit(1);
|
|
}
|
|
if (false == check_lb_rr_graph(lb_rr_graph)) {
|
|
exit(1);
|
|
}
|
|
VTR_LOGV(verbose, "Check routing resource graph for logical tile passed\n");
|
|
|
|
device_annotation.add_physical_lb_rr_graph(lb_type.pb_graph_head,
|
|
lb_rr_graph);
|
|
}
|
|
|
|
VTR_LOGV(verbose, "Done\n");
|
|
}
|
|
|
|
} /* end namespace openfpga */
|