1430 lines
66 KiB
C++
Executable File
1430 lines
66 KiB
C++
Executable File
/************************************************************************
|
|
* This file contains a builder for track-to-track connections inside a
|
|
* tileable General Switch Block (GSB).
|
|
***********************************************************************/
|
|
#include <vector>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
|
|
/* Headers from vtrutil library */
|
|
#include "vtr_assert.h"
|
|
#include "vtr_log.h"
|
|
|
|
/* Headers from openfpgautil library */
|
|
#include "openfpga_side_manager.h"
|
|
|
|
#include "vpr_utils.h"
|
|
#include "rr_graph_obj_util.h"
|
|
#include "openfpga_rr_graph_utils.h"
|
|
#include "rr_graph_builder_utils.h"
|
|
#include "tileable_chan_details_builder.h"
|
|
#include "tileable_rr_graph_gsb.h"
|
|
|
|
/* begin namespace openfpga */
|
|
namespace openfpga {
|
|
|
|
/************************************************************************
|
|
* Internal data structures
|
|
***********************************************************************/
|
|
typedef std::vector<std::vector<int>> t_track_group;
|
|
|
|
/************************************************************************
|
|
* A enumeration to list the status of a track inside a GSB
|
|
* 1. start; 2. end; 3. passing
|
|
* This is used to group tracks which ease the building of
|
|
* track-to-track mapping matrix
|
|
***********************************************************************/
|
|
enum e_track_status {
|
|
TRACK_START,
|
|
TRACK_END,
|
|
TRACK_PASS,
|
|
NUM_TRACK_STATUS /* just a place holder to get the number of status */
|
|
};
|
|
|
|
/************************************************************************
|
|
* Check if a track starts from this GSB or not
|
|
* (xlow, ylow) should be same as the GSB side coordinate
|
|
*
|
|
* Check if a track ends at this GSB or not
|
|
* (xhigh, yhigh) should be same as the GSB side coordinate
|
|
***********************************************************************/
|
|
static
|
|
enum e_track_status determine_track_status_of_gsb(const RRGraph& rr_graph,
|
|
const RRGSB& rr_gsb,
|
|
const enum e_side& gsb_side,
|
|
const size_t& track_id) {
|
|
enum e_track_status track_status = TRACK_PASS;
|
|
/* Get the rr_node */
|
|
RRNodeId track_node = rr_gsb.get_chan_node(gsb_side, track_id);
|
|
/* Get the coordinates */
|
|
vtr::Point<size_t> side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side);
|
|
|
|
/* Get the coordinate of where the track starts */
|
|
vtr::Point<size_t> track_start = get_track_rr_node_start_coordinate(rr_graph, track_node);
|
|
|
|
/* INC_DIRECTION start_track: (xlow, ylow) should be same as the GSB side coordinate */
|
|
/* DEC_DIRECTION start_track: (xhigh, yhigh) should be same as the GSB side coordinate */
|
|
if ( (track_start.x() == side_coordinate.x())
|
|
&& (track_start.y() == side_coordinate.y())
|
|
&& (OUT_PORT == rr_gsb.get_chan_node_direction(gsb_side, track_id)) ) {
|
|
/* Double check: start track should be an OUTPUT PORT of the GSB */
|
|
track_status = TRACK_START;
|
|
}
|
|
|
|
/* Get the coordinate of where the track ends */
|
|
vtr::Point<size_t> track_end = get_track_rr_node_end_coordinate(rr_graph, track_node);
|
|
|
|
/* INC_DIRECTION end_track: (xhigh, yhigh) should be same as the GSB side coordinate */
|
|
/* DEC_DIRECTION end_track: (xlow, ylow) should be same as the GSB side coordinate */
|
|
if ( (track_end.x() == side_coordinate.x())
|
|
&& (track_end.y() == side_coordinate.y())
|
|
&& (IN_PORT == rr_gsb.get_chan_node_direction(gsb_side, track_id)) ) {
|
|
/* Double check: end track should be an INPUT PORT of the GSB */
|
|
track_status = TRACK_END;
|
|
}
|
|
|
|
return track_status;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Check if the GSB is in the Connection Block (CB) population list of the segment
|
|
* SB population of a L4 wire: 1 0 0 1
|
|
*
|
|
* +----+ +----+ +----+ +----+
|
|
* | CB |--->| CB |--->| CB |--->| CB |
|
|
* +----+ +----+ +----+ +----+
|
|
* Engage CB connection Yes No No Yes
|
|
*
|
|
* We will find the offset between gsb_side_coordinate and (xlow,ylow) of the track
|
|
* Use the offset to check if the tracks should engage in this GSB connection
|
|
***********************************************************************/
|
|
static
|
|
bool is_gsb_in_track_cb_population(const RRGraph& rr_graph,
|
|
const RRGSB& rr_gsb,
|
|
const e_side& gsb_side,
|
|
const int& track_id,
|
|
const std::vector<t_segment_inf>& segment_inf) {
|
|
/* Get the rr_node */
|
|
RRNodeId track_node = rr_gsb.get_chan_node(gsb_side, track_id);
|
|
/* Get the coordinates */
|
|
vtr::Point<size_t> side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side);
|
|
|
|
vtr::Point<size_t> track_start = get_track_rr_node_start_coordinate(rr_graph, track_node);
|
|
|
|
/* Get the offset */
|
|
size_t offset = std::abs((int)side_coordinate.x() - (int)track_start.x())
|
|
+ std::abs((int)side_coordinate.y() - (int)track_start.y());
|
|
|
|
/* Get segment id */
|
|
RRSegmentId seg_id = rr_gsb.get_chan_node_segment(gsb_side, track_id);
|
|
/* validate offset */
|
|
VTR_ASSERT(offset < segment_inf[size_t(seg_id)].cb.size());
|
|
|
|
/* Get the SB population */
|
|
bool in_cb_population = false;
|
|
if (true == segment_inf[size_t(seg_id)].cb[offset]) {
|
|
in_cb_population = true;
|
|
}
|
|
|
|
return in_cb_population;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Check if the GSB is in the Switch Block (SB) population list of the segment
|
|
* SB population of a L3 wire: 1 0 0 1
|
|
*
|
|
* +----+ +----+ +----+ +----+
|
|
* | SB |--->| SB |--->| SB |--->| SB |
|
|
* +----+ +----+ +----+ +----+
|
|
* Engage SB connection Yes No No Yes
|
|
*
|
|
* We will find the offset between gsb_side_coordinate and (xlow,ylow) of the track
|
|
* Use the offset to check if the tracks should engage in this GSB connection
|
|
***********************************************************************/
|
|
static
|
|
bool is_gsb_in_track_sb_population(const RRGraph& rr_graph,
|
|
const RRGSB& rr_gsb,
|
|
const e_side& gsb_side,
|
|
const int& track_id,
|
|
const std::vector<t_segment_inf>& segment_inf) {
|
|
/* Get the rr_node */
|
|
const RRNodeId& track_node = rr_gsb.get_chan_node(gsb_side, track_id);
|
|
/* Get the coordinates */
|
|
vtr::Point<size_t> side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side);
|
|
|
|
vtr::Point<size_t> track_start = get_track_rr_node_start_coordinate(rr_graph, track_node);
|
|
|
|
/* Get the offset */
|
|
size_t offset = std::abs((int)side_coordinate.x() - (int)track_start.x())
|
|
+ std::abs((int)side_coordinate.y() - (int)track_start.y());
|
|
|
|
/* Get segment id */
|
|
RRSegmentId seg_id = rr_gsb.get_chan_node_segment(gsb_side, track_id);
|
|
/* validate offset */
|
|
VTR_ASSERT(offset < segment_inf[size_t(seg_id)].sb.size());
|
|
|
|
/* Get the SB population */
|
|
bool in_sb_population = false;
|
|
if (true == segment_inf[size_t(seg_id)].sb[offset]) {
|
|
in_sb_population = true;
|
|
}
|
|
|
|
return in_sb_population;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Create a list of track_id based on the to_track and num_to_tracks
|
|
* We consider the following list [to_track, to_track + Fs/3 - 1]
|
|
* if the [to_track + Fs/3 - 1] exceeds the num_to_tracks, we start over from 0!
|
|
***********************************************************************/
|
|
static
|
|
std::vector<size_t> get_to_track_list(const int& Fs, const int& to_track, const int& num_to_tracks) {
|
|
std::vector<size_t> to_tracks;
|
|
|
|
for (int i = 0; i < Fs; i = i + 3) {
|
|
/* TODO: currently, for Fs > 3, I always search the next from_track until Fs is satisfied
|
|
* The optimal track selection should be done in a more scientific way!!!
|
|
*/
|
|
int to_track_i = to_track + i;
|
|
/* make sure the track id is still in range */
|
|
if ( to_track_i > num_to_tracks - 1) {
|
|
to_track_i = to_track_i % num_to_tracks;
|
|
}
|
|
/* Ensure we are in the range */
|
|
VTR_ASSERT(to_track_i < num_to_tracks);
|
|
/* from track must be connected */
|
|
to_tracks.push_back(to_track_i);
|
|
}
|
|
return to_tracks;
|
|
}
|
|
|
|
/************************************************************************
|
|
* This function aims to return the track indices that drive the from_track
|
|
* in a Switch Block
|
|
* The track_ids to return will depend on different topologies of SB
|
|
* SUBSET, UNIVERSAL, and WILTON.
|
|
***********************************************************************/
|
|
static
|
|
std::vector<size_t> get_switch_block_to_track_id(const e_switch_block_type& switch_block_type,
|
|
const int& Fs,
|
|
const e_side& from_side,
|
|
const int& from_track,
|
|
const e_side& to_side,
|
|
const int& num_to_tracks) {
|
|
|
|
/* This routine returns the track number to which the from_track should
|
|
* connect. It supports any Fs % 3 == 0, switch blocks.
|
|
*/
|
|
std::vector<size_t> to_tracks;
|
|
|
|
/* TODO: currently, for Fs > 3, I always search the next from_track until Fs is satisfied
|
|
* The optimal track selection should be done in a more scientific way!!!
|
|
*/
|
|
VTR_ASSERT(0 == Fs % 3);
|
|
|
|
/* Adapt from_track to fit in the range of num_to_tracks */
|
|
size_t actual_from_track = from_track % num_to_tracks;
|
|
|
|
switch (switch_block_type) {
|
|
case SUBSET: /* NB: Global routing uses SUBSET too */
|
|
to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks);
|
|
/* Finish, we return */
|
|
return to_tracks;
|
|
case UNIVERSAL:
|
|
if ( (from_side == LEFT)
|
|
|| (from_side == RIGHT) ) {
|
|
/* For the prev_side, to_track is from_track
|
|
* For the next_side, to_track is num_to_tracks - 1 - from_track
|
|
* For the opposite_side, to_track is always from_track
|
|
*/
|
|
SideManager side_manager(from_side);
|
|
if ( (to_side == side_manager.get_opposite())
|
|
|| (to_side == side_manager.get_rotate_counterclockwise()) ) {
|
|
to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks);
|
|
} else if (to_side == side_manager.get_rotate_clockwise()) {
|
|
to_tracks = get_to_track_list(Fs, num_to_tracks - 1 - actual_from_track, num_to_tracks);
|
|
}
|
|
}
|
|
|
|
if ( (from_side == TOP)
|
|
|| (from_side == BOTTOM) ) {
|
|
/* For the next_side, to_track is from_track
|
|
* For the prev_side, to_track is num_to_tracks - 1 - from_track
|
|
* For the opposite_side, to_track is always from_track
|
|
*/
|
|
SideManager side_manager(from_side);
|
|
if ( (to_side == side_manager.get_opposite())
|
|
|| (to_side == side_manager.get_rotate_clockwise()) ) {
|
|
to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks);
|
|
} else if (to_side == side_manager.get_rotate_counterclockwise()) {
|
|
to_tracks = get_to_track_list(Fs, num_to_tracks - 1 - actual_from_track, num_to_tracks);
|
|
}
|
|
}
|
|
/* Finish, we return */
|
|
return to_tracks;
|
|
/* End switch_block_type == UNIVERSAL case. */
|
|
case WILTON:
|
|
/* See S. Wilton Phd thesis, U of T, 1996 p. 103 for details on following. */
|
|
if (from_side == LEFT) {
|
|
if (to_side == RIGHT) { /* CHANX to CHANX */
|
|
to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks);
|
|
} else if (to_side == TOP) { /* from CHANX to CHANY */
|
|
to_tracks = get_to_track_list(Fs, (num_to_tracks - actual_from_track ) % num_to_tracks, num_to_tracks);
|
|
} else if (to_side == BOTTOM) {
|
|
to_tracks = get_to_track_list(Fs, (num_to_tracks + actual_from_track - 1) % num_to_tracks, num_to_tracks);
|
|
}
|
|
} else if (from_side == RIGHT) {
|
|
if (to_side == LEFT) { /* CHANX to CHANX */
|
|
to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks);
|
|
} else if (to_side == TOP) { /* from CHANX to CHANY */
|
|
to_tracks = get_to_track_list(Fs, (num_to_tracks + actual_from_track - 1) % num_to_tracks, num_to_tracks);
|
|
} else if (to_side == BOTTOM) {
|
|
to_tracks = get_to_track_list(Fs, (2 * num_to_tracks - 2 - actual_from_track) % num_to_tracks, num_to_tracks);
|
|
}
|
|
} else if (from_side == BOTTOM) {
|
|
if (to_side == TOP) { /* CHANY to CHANY */
|
|
to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks);
|
|
} else if (to_side == LEFT) { /* from CHANY to CHANX */
|
|
to_tracks = get_to_track_list(Fs, (actual_from_track + 1) % num_to_tracks, num_to_tracks);
|
|
} else if (to_side == RIGHT) {
|
|
to_tracks = get_to_track_list(Fs, (2 * num_to_tracks - 2 - actual_from_track) % num_to_tracks, num_to_tracks);
|
|
}
|
|
} else if (from_side == TOP) {
|
|
if (to_side == BOTTOM) { /* CHANY to CHANY */
|
|
to_tracks = get_to_track_list(Fs, from_track, num_to_tracks);
|
|
} else if (to_side == LEFT) { /* from CHANY to CHANX */
|
|
to_tracks = get_to_track_list(Fs, (num_to_tracks - actual_from_track) % num_to_tracks, num_to_tracks);
|
|
} else if (to_side == RIGHT) {
|
|
to_tracks = get_to_track_list(Fs, (actual_from_track + 1) % num_to_tracks, num_to_tracks);
|
|
}
|
|
}
|
|
/* Finish, we return */
|
|
return to_tracks;
|
|
/* End switch_block_type == WILTON case. */
|
|
default:
|
|
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
|
"Invalid switch block pattern !\n");
|
|
exit(1);
|
|
}
|
|
|
|
return to_tracks;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* Build the track_to_track_map[from_side][0..chan_width-1][to_side][track_indices]
|
|
* For a group of from_track nodes and to_track nodes
|
|
* For each side of from_tracks, we call a routine to get the list of to_tracks
|
|
* Then, we fill the track2track_map
|
|
***********************************************************************/
|
|
static
|
|
void build_gsb_one_group_track_to_track_map(const RRGraph& rr_graph,
|
|
const RRGSB& rr_gsb,
|
|
const e_switch_block_type& sb_type,
|
|
const int& Fs,
|
|
const bool& wire_opposite_side,
|
|
const t_track_group& from_tracks, /* [0..gsb_side][track_indices] */
|
|
const t_track_group& to_tracks, /* [0..gsb_side][track_indices] */
|
|
t_track2track_map& track2track_map) {
|
|
for (size_t side = 0; side < from_tracks.size(); ++side) {
|
|
SideManager side_manager(side);
|
|
e_side from_side = side_manager.get_side();
|
|
/* Find the other sides where the start tracks will locate */
|
|
std::vector<e_side> to_track_sides;
|
|
/* 0. opposite side */
|
|
to_track_sides.push_back(side_manager.get_opposite());
|
|
/* 1. prev side */
|
|
/* Previous side definition: TOP => LEFT; RIGHT=>TOP; BOTTOM=>RIGHT; LEFT=>BOTTOM */
|
|
to_track_sides.push_back(side_manager.get_rotate_counterclockwise());
|
|
/* 2. next side */
|
|
/* Next side definition: TOP => RIGHT; RIGHT=>BOTTOM; BOTTOM=>LEFT; LEFT=>TOP */
|
|
to_track_sides.push_back(side_manager.get_rotate_clockwise());
|
|
|
|
for (size_t inode = 0; inode < from_tracks[side].size(); ++inode) {
|
|
for (size_t to_side_id = 0; to_side_id < to_track_sides.size(); ++to_side_id) {
|
|
enum e_side to_side = to_track_sides[to_side_id];
|
|
SideManager to_side_manager(to_side);
|
|
size_t to_side_index = to_side_manager.to_size_t();
|
|
/* Bypass those to_sides have no nodes */
|
|
if (0 == to_tracks[to_side_index].size()) {
|
|
continue;
|
|
}
|
|
/* Bypass those from_side is same as to_side */
|
|
if (from_side == to_side) {
|
|
continue;
|
|
}
|
|
/* Bypass those from_side is opposite to to_side if required */
|
|
if ( (true == wire_opposite_side)
|
|
&& (to_side_manager.get_opposite() == from_side) ) {
|
|
continue;
|
|
}
|
|
/* Get other track_ids depending on the switch block pattern */
|
|
/* Find the track ids that will start at the other sides */
|
|
std::vector<size_t> to_track_ids = get_switch_block_to_track_id(sb_type, Fs, from_side, inode,
|
|
to_side,
|
|
to_tracks[to_side_index].size());
|
|
/* Update the track2track_map: */
|
|
for (size_t to_track_id = 0; to_track_id < to_track_ids.size(); ++to_track_id) {
|
|
size_t from_side_index = side_manager.to_size_t();
|
|
size_t from_track_index = from_tracks[side][inode];
|
|
/* Check the id is still in the range !*/
|
|
VTR_ASSERT( to_track_ids[to_track_id] < to_tracks[to_side_index].size() );
|
|
size_t to_track_index = to_tracks[to_side_index][to_track_ids[to_track_id]];
|
|
//printf("from_track(size=%lu): %lu , to_track_ids[%lu]:%lu, to_track_index: %lu in a group of %lu tracks\n",
|
|
// from_tracks[side].size(), inode, to_track_id, to_track_ids[to_track_id],
|
|
// to_track_index, to_tracks[to_side_index].size());
|
|
const RRNodeId& to_track_node = rr_gsb.get_chan_node(to_side, to_track_index);
|
|
VTR_ASSERT(true == rr_graph.valid_node_id(to_track_node));
|
|
|
|
/* from_track should be IN_PORT */
|
|
VTR_ASSERT(IN_PORT == rr_gsb.get_chan_node_direction(from_side, from_track_index));
|
|
/* to_track should be OUT_PORT */
|
|
VTR_ASSERT(OUT_PORT == rr_gsb.get_chan_node_direction(to_side, to_track_index));
|
|
|
|
/* Check if the to_track_node is already in the list ! */
|
|
std::vector<RRNodeId>::iterator it = std::find(track2track_map[from_side_index][from_track_index].begin(),
|
|
track2track_map[from_side_index][from_track_index].end(),
|
|
to_track_node);
|
|
if (it != track2track_map[from_side_index][from_track_index].end()) {
|
|
continue; /* the node_id is already in the list, go for the next */
|
|
}
|
|
/* Clear, we should add to the list */
|
|
track2track_map[from_side_index][from_track_index].push_back(to_track_node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Build the track_to_track_map[from_side][0..chan_width-1][to_side][track_indices]
|
|
* based on the existing routing resources in the General Switch Block (GSB)
|
|
* The track_indices is the indices of tracks that the node at from_side and [0..chan_width-1] will drive
|
|
* IMPORTANT: the track_indices are the indicies in the GSB context, but not the rr_graph!!!
|
|
* We separate the connections into two groups:
|
|
* Group 1: the routing tracks start from this GSB
|
|
* We will apply switch block patterns (SUBSET, UNIVERSAL, WILTON)
|
|
* Group 2: the routing tracks do not start from this GSB (bypassing wires)
|
|
* We will apply switch block patterns (SUBSET, UNIVERSAL, WILTON)
|
|
* but we will check the Switch Block (SB) population of these
|
|
* routing segments, and determine which requires connections
|
|
*
|
|
* CHANY CHANY CHANY CHANY
|
|
* [0] [1] [2] [3]
|
|
* start yes no yes no
|
|
* end +-------------------------+ start Group 1 Group 2
|
|
* no CHANX[0] | TOP | CHANX[0] yes TOP/BOTTOM TOP/BOTTOM
|
|
* | | CHANY[0,2] CHANY[1,3]
|
|
* yes CHANX[1] | | CHANX[1] no
|
|
* | LEFT RIGHT |
|
|
* no CHANX[2] | | CHANX[2] yes
|
|
* | |
|
|
* yes CHANX[3] | BOTTOM | CHANX[3] no
|
|
* +-------------------------+
|
|
* CHANY CHANY CHANY CHANY
|
|
* [0] [1] [2] [3]
|
|
* start yes no yes no
|
|
*
|
|
* The mapping is done in the following steps: (For each side of the GSB)
|
|
* 1. Build a list of tracks that will start from this side
|
|
* if a track starts, its xlow/ylow is the same as the x,y of this gsb
|
|
* 2. Build a list of tracks on the other sides belonging to Group 1.
|
|
* Take the example of RIGHT side, we will collect
|
|
* a. tracks that will end at the LEFT side
|
|
* b. tracks that will start at the TOP side
|
|
* c. tracks that will start at the BOTTOM side
|
|
* 3. Apply switch block patterns to Group 1 (SUBSET, UNIVERSAL, WILTON)
|
|
* 4. Build a list of tracks on the other sides belonging to Group 1.
|
|
* Take the example of RIGHT side, we will collect
|
|
* a. tracks that will bypass at the TOP side
|
|
* b. tracks that will bypass at the BOTTOM side
|
|
* 5. Apply switch block patterns to Group 2 (SUBSET, UNIVERSAL, WILTON)
|
|
***********************************************************************/
|
|
t_track2track_map build_gsb_track_to_track_map(const RRGraph& rr_graph,
|
|
const RRGSB& rr_gsb,
|
|
const e_switch_block_type& sb_type,
|
|
const int& Fs,
|
|
const e_switch_block_type& sb_subtype,
|
|
const int& subFs,
|
|
const bool& wire_opposite_side,
|
|
const std::vector<t_segment_inf>& segment_inf) {
|
|
t_track2track_map track2track_map; /* [0..gsb_side][0..chan_width][track_indices] */
|
|
|
|
/* Categorize tracks into 3 groups:
|
|
* (1) tracks will start here
|
|
* (2) tracks will end here
|
|
* (2) tracks will just pass through the SB */
|
|
t_track_group start_tracks; /* [0..gsb_side][track_indices] */
|
|
t_track_group end_tracks; /* [0..gsb_side][track_indices] */
|
|
t_track_group pass_tracks; /* [0..gsb_side][track_indices] */
|
|
|
|
/* resize to the number of sides */
|
|
start_tracks.resize(rr_gsb.get_num_sides());
|
|
end_tracks.resize(rr_gsb.get_num_sides());
|
|
pass_tracks.resize(rr_gsb.get_num_sides());
|
|
|
|
/* Walk through each side */
|
|
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
|
SideManager side_manager(side);
|
|
e_side gsb_side = side_manager.get_side();
|
|
/* Build a list of tracks that will start from this side */
|
|
for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) {
|
|
/* We need to check Switch block population of this track
|
|
* The track node will not be considered if there supposed to be no SB at this position
|
|
*/
|
|
if (false == is_gsb_in_track_sb_population(rr_graph, rr_gsb, gsb_side, inode, segment_inf)) {
|
|
continue; /* skip this node and go to the next */
|
|
}
|
|
/* check if this track will start from here */
|
|
enum e_track_status track_status = determine_track_status_of_gsb(rr_graph, rr_gsb, gsb_side, inode);
|
|
|
|
switch (track_status) {
|
|
case TRACK_START:
|
|
/* update starting track list */
|
|
start_tracks[side].push_back(inode);
|
|
break;
|
|
case TRACK_END:
|
|
/* Update end track list */
|
|
end_tracks[side].push_back(inode);
|
|
break;
|
|
case TRACK_PASS:
|
|
/* Update passing track list */
|
|
/* Note that the pass_track should be IN_PORT only !!! */
|
|
if (IN_PORT == rr_gsb.get_chan_node_direction(gsb_side, inode)) {
|
|
pass_tracks[side].push_back(inode);
|
|
}
|
|
break;
|
|
default:
|
|
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
|
"Invalid track status!\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Allocate track2track map */
|
|
track2track_map.resize(rr_gsb.get_num_sides());
|
|
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
|
SideManager side_manager(side);
|
|
enum e_side gsb_side = side_manager.get_side();
|
|
/* allocate track2track_map[gsb_side] */
|
|
track2track_map[side].resize(rr_gsb.get_chan_width(gsb_side));
|
|
for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) {
|
|
/* allocate track2track_map[gsb_side][inode] */
|
|
track2track_map[side][inode].clear();
|
|
}
|
|
}
|
|
|
|
/* For Group 1: we build connections between end_tracks and start_tracks*/
|
|
build_gsb_one_group_track_to_track_map(rr_graph, rr_gsb,
|
|
sb_type, Fs,
|
|
true, /* End tracks should always to wired to start tracks */
|
|
end_tracks, start_tracks,
|
|
track2track_map);
|
|
|
|
/* For Group 2: we build connections between end_tracks and start_tracks*/
|
|
/* Currently, I use the same Switch Block pattern for the passing tracks and end tracks,
|
|
* TODO: This can be improved with different patterns!
|
|
*/
|
|
build_gsb_one_group_track_to_track_map(rr_graph, rr_gsb,
|
|
sb_subtype, subFs,
|
|
wire_opposite_side, /* Pass tracks may not be wired to start tracks */
|
|
pass_tracks, start_tracks,
|
|
track2track_map);
|
|
|
|
return track2track_map;
|
|
}
|
|
|
|
/* Build a RRChan Object with the given channel type and coorindators */
|
|
static
|
|
RRChan build_one_tileable_rr_chan(const vtr::Point<size_t>& chan_coordinate,
|
|
const t_rr_type& chan_type,
|
|
const RRGraph& rr_graph,
|
|
const ChanNodeDetails& chan_details) {
|
|
std::vector<RRNodeId> chan_rr_nodes;
|
|
|
|
/* Create a rr_chan object and check if it is unique in the graph */
|
|
RRChan rr_chan;
|
|
|
|
/* Fill the information */
|
|
rr_chan.set_type(chan_type);
|
|
|
|
/* Collect rr_nodes for this channel */
|
|
chan_rr_nodes = find_rr_graph_chan_nodes(rr_graph,
|
|
chan_coordinate.x(), chan_coordinate.y(),
|
|
chan_type);
|
|
|
|
/* Reserve */
|
|
/* rr_chan.reserve_node(size_t(chan_width)); */
|
|
|
|
/* Fill the rr_chan */
|
|
for (size_t itrack = 0; itrack < chan_rr_nodes.size(); ++itrack) {
|
|
size_t iseg = chan_details.get_track_segment_id(itrack);
|
|
rr_chan.add_node(rr_graph, chan_rr_nodes[itrack], RRSegmentId(iseg));
|
|
}
|
|
|
|
return rr_chan;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Build a General Switch Block (GSB)
|
|
* which includes:
|
|
* [I] A Switch Box subckt consists of following ports:
|
|
* 1. Channel Y [x][y] inputs
|
|
* 2. Channel X [x+1][y] inputs
|
|
* 3. Channel Y [x][y-1] outputs
|
|
* 4. Channel X [x][y] outputs
|
|
* 5. Grid[x][y+1] Right side outputs pins
|
|
* 6. Grid[x+1][y+1] Left side output pins
|
|
* 7. Grid[x+1][y+1] Bottom side output pins
|
|
* 8. Grid[x+1][y] Top side output pins
|
|
* 9. Grid[x+1][y] Left side output pins
|
|
* 10. Grid[x][y] Right side output pins
|
|
* 11. Grid[x][y] Top side output pins
|
|
* 12. Grid[x][y+1] Bottom side output pins
|
|
*
|
|
* -------------- --------------
|
|
* | | CBY | |
|
|
* | Grid | ChanY | Grid |
|
|
* | [x][y+1] | [x][y+1] | [x+1][y+1] |
|
|
* | | | |
|
|
* -------------- --------------
|
|
* ----------
|
|
* ChanX & CBX | Switch | ChanX
|
|
* [x][y] | Box | [x+1][y]
|
|
* | [x][y] |
|
|
* ----------
|
|
* -------------- --------------
|
|
* | | | |
|
|
* | Grid | ChanY | Grid |
|
|
* | [x][y] | [x][y] | [x+1][y] |
|
|
* | | | |
|
|
* -------------- --------------
|
|
* For channels chanY with INC_DIRECTION on the top side, they should be marked as outputs
|
|
* For channels chanY with DEC_DIRECTION on the top side, they should be marked as inputs
|
|
* For channels chanY with INC_DIRECTION on the bottom side, they should be marked as inputs
|
|
* For channels chanY with DEC_DIRECTION on the bottom side, they should be marked as outputs
|
|
* For channels chanX with INC_DIRECTION on the left side, they should be marked as inputs
|
|
* For channels chanX with DEC_DIRECTION on the left side, they should be marked as outputs
|
|
* For channels chanX with INC_DIRECTION on the right side, they should be marked as outputs
|
|
* For channels chanX with DEC_DIRECTION on the right side, they should be marked as inputs
|
|
*
|
|
* [II] A X-direction Connection Block [x][y]
|
|
* The connection block shares the same routing channel[x][y] with the Switch Block
|
|
* We just need to fill the ipin nodes at TOP and BOTTOM sides
|
|
* as well as properly fill the ipin_grid_side information
|
|
* [III] A Y-direction Connection Block [x][y+1]
|
|
* The connection block shares the same routing channel[x][y+1] with the Switch Block
|
|
* We just need to fill the ipin nodes at LEFT and RIGHT sides
|
|
* as well as properly fill the ipin_grid_side information
|
|
***********************************************************************/
|
|
RRGSB build_one_tileable_rr_gsb(const DeviceGrid& grids,
|
|
const RRGraph& rr_graph,
|
|
const vtr::Point<size_t>& device_chan_width,
|
|
const std::vector<t_segment_inf>& segment_inf,
|
|
const vtr::Point<size_t>& gsb_coordinate) {
|
|
/* Create an object to return */
|
|
RRGSB rr_gsb;
|
|
|
|
/* Check */
|
|
VTR_ASSERT(gsb_coordinate.x() <= grids.width());
|
|
VTR_ASSERT(gsb_coordinate.y() <= grids.height());
|
|
|
|
/* Coordinator initialization */
|
|
rr_gsb.set_coordinate(gsb_coordinate.x(), gsb_coordinate.y());
|
|
|
|
/* Basic information*/
|
|
rr_gsb.init_num_sides(4); /* Fixed number of sides */
|
|
|
|
/* Find all rr_nodes of channels */
|
|
/* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */
|
|
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
|
/* Local variables inside this for loop */
|
|
SideManager side_manager(side);
|
|
vtr::Point<size_t> coordinate = rr_gsb.get_side_block_coordinate(side_manager.get_side());
|
|
RRChan rr_chan;
|
|
std::vector<std::vector<RRNodeId>> temp_opin_rr_nodes(2);
|
|
enum e_side opin_grid_side[2] = {NUM_SIDES, NUM_SIDES};
|
|
enum PORTS chan_dir_to_port_dir_mapping[2] = {OUT_PORT, IN_PORT}; /* 0: INC_DIRECTION => ?; 1: DEC_DIRECTION => ? */
|
|
|
|
/* Build a segment details, where we need the segment ids for building rr_chan
|
|
* We do not care starting and ending points here, so set chan_side as NUM_SIDES
|
|
*/
|
|
ChanNodeDetails chanx_details = build_unidir_chan_node_details(device_chan_width.x(), grids.width() - 1,
|
|
false, false, segment_inf);
|
|
ChanNodeDetails chany_details = build_unidir_chan_node_details(device_chan_width.y(), grids.height() - 1,
|
|
false, false, segment_inf);
|
|
|
|
switch (side) {
|
|
case TOP: /* TOP = 0 */
|
|
/* For the bording, we should take special care */
|
|
if (gsb_coordinate.y() == grids.height() - 1) {
|
|
rr_gsb.clear_one_side(side_manager.get_side());
|
|
break;
|
|
}
|
|
/* Routing channels*/
|
|
/* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */
|
|
/* Create a rr_chan object and check if it is unique in the graph */
|
|
rr_chan = build_one_tileable_rr_chan(coordinate, CHANY, rr_graph, chany_details);
|
|
chan_dir_to_port_dir_mapping[0] = OUT_PORT; /* INC_DIRECTION => OUT_PORT */
|
|
chan_dir_to_port_dir_mapping[1] = IN_PORT; /* DEC_DIRECTION => IN_PORT */
|
|
|
|
/* Assign grid side of OPIN */
|
|
/* Grid[x][y+1] RIGHT side outputs pins */
|
|
opin_grid_side[0] = RIGHT;
|
|
/* Grid[x+1][y+1] left side outputs pins */
|
|
opin_grid_side[1] = LEFT;
|
|
|
|
/* Build the Switch block: opin and opin_grid_side */
|
|
/* Include Grid[x][y+1] RIGHT side outputs pins */
|
|
temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids,
|
|
gsb_coordinate.x(), gsb_coordinate.y() + 1,
|
|
OPIN, opin_grid_side[0]);
|
|
/* Include Grid[x+1][y+1] Left side output pins */
|
|
temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids,
|
|
gsb_coordinate.x() + 1, gsb_coordinate.y() + 1,
|
|
OPIN, opin_grid_side[1]);
|
|
|
|
break;
|
|
case RIGHT: /* RIGHT = 1 */
|
|
/* For the bording, we should take special care */
|
|
if (gsb_coordinate.x() == grids.width() - 1) {
|
|
rr_gsb.clear_one_side(side_manager.get_side());
|
|
break;
|
|
}
|
|
/* Routing channels*/
|
|
/* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */
|
|
/* Collect rr_nodes for Tracks for top: chany[x][y+1] */
|
|
/* Create a rr_chan object and check if it is unique in the graph */
|
|
rr_chan = build_one_tileable_rr_chan(coordinate, CHANX, rr_graph, chanx_details);
|
|
chan_dir_to_port_dir_mapping[0] = OUT_PORT; /* INC_DIRECTION => OUT_PORT */
|
|
chan_dir_to_port_dir_mapping[1] = IN_PORT; /* DEC_DIRECTION => IN_PORT */
|
|
|
|
/* Assign grid side of OPIN */
|
|
/* Grid[x+1][y+1] BOTTOM side outputs pins */
|
|
opin_grid_side[0] = BOTTOM;
|
|
/* Grid[x+1][y] TOP side outputs pins */
|
|
opin_grid_side[1] = TOP;
|
|
|
|
/* Build the Switch block: opin and opin_grid_side */
|
|
/* include Grid[x+1][y+1] Bottom side output pins */
|
|
temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids,
|
|
gsb_coordinate.x() + 1, gsb_coordinate.y() + 1,
|
|
OPIN, opin_grid_side[0]);
|
|
/* include Grid[x+1][y] Top side output pins */
|
|
temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids,
|
|
gsb_coordinate.x() + 1, gsb_coordinate.y(),
|
|
OPIN, opin_grid_side[1]);
|
|
break;
|
|
case BOTTOM: /* BOTTOM = 2*/
|
|
/* For the bording, we should take special care */
|
|
if (gsb_coordinate.y() == 0) {
|
|
rr_gsb.clear_one_side(side_manager.get_side());
|
|
break;
|
|
}
|
|
/* Routing channels*/
|
|
/* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */
|
|
/* Collect rr_nodes for Tracks for bottom: chany[x][y] */
|
|
/* Create a rr_chan object and check if it is unique in the graph */
|
|
rr_chan = build_one_tileable_rr_chan(coordinate, CHANY, rr_graph, chany_details);
|
|
chan_dir_to_port_dir_mapping[0] = IN_PORT; /* INC_DIRECTION => IN_PORT */
|
|
chan_dir_to_port_dir_mapping[1] = OUT_PORT; /* DEC_DIRECTION => OUT_PORT */
|
|
|
|
/* Assign grid side of OPIN */
|
|
/* Grid[x+1][y] LEFT side outputs pins */
|
|
opin_grid_side[0] = LEFT;
|
|
/* Grid[x][y] RIGHT side outputs pins */
|
|
opin_grid_side[1] = RIGHT;
|
|
|
|
/* Build the Switch block: opin and opin_grid_side */
|
|
/* include Grid[x+1][y] Left side output pins */
|
|
temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids,
|
|
gsb_coordinate.x() + 1, gsb_coordinate.y(),
|
|
OPIN, opin_grid_side[0]);
|
|
/* include Grid[x][y] Right side output pins */
|
|
temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids,
|
|
gsb_coordinate.x(), gsb_coordinate.y(),
|
|
OPIN, opin_grid_side[1]);
|
|
break;
|
|
case LEFT: /* LEFT = 3 */
|
|
/* For the bording, we should take special care */
|
|
if (gsb_coordinate.x() == 0) {
|
|
rr_gsb.clear_one_side(side_manager.get_side());
|
|
break;
|
|
}
|
|
/* Routing channels*/
|
|
/* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */
|
|
/* Collect rr_nodes for Tracks for left: chanx[x][y] */
|
|
/* Create a rr_chan object and check if it is unique in the graph */
|
|
rr_chan = build_one_tileable_rr_chan(coordinate, CHANX, rr_graph, chanx_details);
|
|
chan_dir_to_port_dir_mapping[0] = IN_PORT; /* INC_DIRECTION => IN_PORT */
|
|
chan_dir_to_port_dir_mapping[1] = OUT_PORT; /* DEC_DIRECTION => OUT_PORT */
|
|
|
|
/* Grid[x][y+1] BOTTOM side outputs pins */
|
|
opin_grid_side[0] = BOTTOM;
|
|
/* Grid[x][y] TOP side outputs pins */
|
|
opin_grid_side[1] = TOP;
|
|
|
|
/* Build the Switch block: opin and opin_grid_side */
|
|
/* include Grid[x][y+1] Bottom side outputs pins */
|
|
temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids,
|
|
gsb_coordinate.x(), gsb_coordinate.y() + 1,
|
|
OPIN, opin_grid_side[0]);
|
|
/* include Grid[x][y] Top side output pins */
|
|
temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids,
|
|
gsb_coordinate.x(), gsb_coordinate.y(),
|
|
OPIN, opin_grid_side[1]);
|
|
|
|
break;
|
|
default:
|
|
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
|
"Invalid side index!\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Organize a vector of port direction */
|
|
if (0 < rr_chan.get_chan_width()) {
|
|
std::vector<enum PORTS> rr_chan_dir;
|
|
rr_chan_dir.resize(rr_chan.get_chan_width());
|
|
for (size_t itrack = 0; itrack < rr_chan.get_chan_width(); ++itrack) {
|
|
/* Identify the directionality, record it in rr_node_direction */
|
|
if (INC_DIRECTION == rr_graph.node_direction(rr_chan.get_node(itrack))) {
|
|
rr_chan_dir[itrack] = chan_dir_to_port_dir_mapping[0];
|
|
} else {
|
|
VTR_ASSERT(DEC_DIRECTION == rr_graph.node_direction(rr_chan.get_node(itrack)));
|
|
rr_chan_dir[itrack] = chan_dir_to_port_dir_mapping[1];
|
|
}
|
|
}
|
|
/* Fill chan_rr_nodes */
|
|
rr_gsb.add_chan_node(side_manager.get_side(), rr_chan, rr_chan_dir);
|
|
}
|
|
|
|
/* Fill opin_rr_nodes */
|
|
/* Copy from temp_opin_rr_node to opin_rr_node */
|
|
for (const RRNodeId& inode : temp_opin_rr_nodes[0]) {
|
|
/* Grid[x+1][y+1] Bottom side outputs pins */
|
|
rr_gsb.add_opin_node(inode, side_manager.get_side());
|
|
}
|
|
for (const RRNodeId& inode : temp_opin_rr_nodes[1]) {
|
|
/* Grid[x+1][y] TOP side outputs pins */
|
|
rr_gsb.add_opin_node(inode, side_manager.get_side());
|
|
}
|
|
|
|
/* Clean ipin_rr_nodes */
|
|
/* We do not have any IPIN for a Switch Block */
|
|
rr_gsb.clear_ipin_nodes(side_manager.get_side());
|
|
|
|
/* Clear the temp data */
|
|
temp_opin_rr_nodes[0].clear();
|
|
temp_opin_rr_nodes[1].clear();
|
|
opin_grid_side[0] = NUM_SIDES;
|
|
opin_grid_side[1] = NUM_SIDES;
|
|
}
|
|
|
|
/* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */
|
|
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
|
/* Local variables inside this for loop */
|
|
SideManager side_manager(side);
|
|
size_t ix;
|
|
size_t iy;
|
|
enum e_side chan_side;
|
|
std::vector<RRNodeId> temp_ipin_rr_nodes;
|
|
enum e_side ipin_rr_node_grid_side;
|
|
|
|
switch (side) {
|
|
case TOP: /* TOP = 0 */
|
|
/* For the bording, we should take special care */
|
|
/* Check if left side chan width is 0 or not */
|
|
chan_side = LEFT;
|
|
/* Build the connection block: ipin and ipin_grid_side */
|
|
/* BOTTOM side INPUT Pins of Grid[x][y+1] */
|
|
ix = rr_gsb.get_sb_x();
|
|
iy = rr_gsb.get_sb_y() + 1;
|
|
ipin_rr_node_grid_side = BOTTOM;
|
|
break;
|
|
case RIGHT: /* RIGHT = 1 */
|
|
/* For the bording, we should take special care */
|
|
/* Check if TOP side chan width is 0 or not */
|
|
chan_side = TOP;
|
|
/* Build the connection block: ipin and ipin_grid_side */
|
|
/* LEFT side INPUT Pins of Grid[x+1][y+1] */
|
|
ix = rr_gsb.get_sb_x() + 1;
|
|
iy = rr_gsb.get_sb_y() + 1;
|
|
ipin_rr_node_grid_side = LEFT;
|
|
break;
|
|
case BOTTOM: /* BOTTOM = 2*/
|
|
/* For the bording, we should take special care */
|
|
/* Check if left side chan width is 0 or not */
|
|
chan_side = LEFT;
|
|
/* Build the connection block: ipin and ipin_grid_side */
|
|
/* TOP side INPUT Pins of Grid[x][y] */
|
|
ix = rr_gsb.get_sb_x();
|
|
iy = rr_gsb.get_sb_y();
|
|
ipin_rr_node_grid_side = TOP;
|
|
break;
|
|
case LEFT: /* LEFT = 3 */
|
|
/* For the bording, we should take special care */
|
|
/* Check if left side chan width is 0 or not */
|
|
chan_side = TOP;
|
|
/* Build the connection block: ipin and ipin_grid_side */
|
|
/* RIGHT side INPUT Pins of Grid[x][y+1] */
|
|
ix = rr_gsb.get_sb_x();
|
|
iy = rr_gsb.get_sb_y() + 1;
|
|
ipin_rr_node_grid_side = RIGHT;
|
|
break;
|
|
default:
|
|
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
|
"Invalid side index!\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* If there is no channel at this side, we skip ipin_node annotation */
|
|
if (0 == rr_gsb.get_chan_width(chan_side)) {
|
|
continue;
|
|
}
|
|
/* Collect IPIN rr_nodes*/
|
|
temp_ipin_rr_nodes = find_rr_graph_grid_nodes(rr_graph, grids,
|
|
ix, iy, IPIN, ipin_rr_node_grid_side);
|
|
/* Fill the ipin nodes of RRGSB */
|
|
for (const RRNodeId& inode : temp_ipin_rr_nodes) {
|
|
rr_gsb.add_ipin_node(inode, side_manager.get_side());
|
|
}
|
|
/* Clear the temp data */
|
|
temp_ipin_rr_nodes.clear();
|
|
}
|
|
|
|
return rr_gsb;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Create edges for each rr_node of a General Switch Blocks (GSB):
|
|
* 1. create edges between CHANX | CHANY and IPINs (connections inside connection blocks)
|
|
* 2. create edges between OPINs, CHANX and CHANY (connections inside switch blocks)
|
|
* 3. create edges between OPINs and IPINs (direct-connections)
|
|
***********************************************************************/
|
|
void build_edges_for_one_tileable_rr_gsb(RRGraph& rr_graph,
|
|
const RRGSB& rr_gsb,
|
|
const t_track2pin_map& track2ipin_map,
|
|
const t_pin2track_map& opin2track_map,
|
|
const t_track2track_map& track2track_map,
|
|
const vtr::vector<RRNodeId, RRSwitchId>& rr_node_driver_switches) {
|
|
|
|
/* Walk through each sides */
|
|
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
|
SideManager side_manager(side);
|
|
enum e_side gsb_side = side_manager.get_side();
|
|
|
|
/* Find OPINs */
|
|
for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(gsb_side); ++inode) {
|
|
const RRNodeId& opin_node = rr_gsb.get_opin_node(gsb_side, inode);
|
|
|
|
/* 1. create edges between OPINs and CHANX|CHANY, using opin2track_map */
|
|
/* add edges to the opin_node */
|
|
for (const RRNodeId& track_node : opin2track_map[gsb_side][inode]) {
|
|
rr_graph.create_edge(opin_node, track_node, rr_node_driver_switches[track_node]);
|
|
}
|
|
}
|
|
|
|
/* Find CHANX or CHANY */
|
|
/* For TRACKs to IPINs, we only care LEFT and TOP sides
|
|
* Skip RIGHT and BOTTOM for the ipin2track_map since they should be handled in other GSBs
|
|
*/
|
|
if ( (side_manager.get_side() == rr_gsb.get_cb_chan_side(CHANX))
|
|
|| (side_manager.get_side() == rr_gsb.get_cb_chan_side(CHANY)) ) {
|
|
/* 2. create edges between CHANX|CHANY and IPINs, using ipin2track_map */
|
|
for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) {
|
|
const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode);
|
|
for (const RRNodeId& ipin_node : track2ipin_map[gsb_side][inode]) {
|
|
rr_graph.create_edge(chan_node, ipin_node, rr_node_driver_switches[ipin_node]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 3. create edges between CHANX|CHANY and CHANX|CHANY, using track2track_map */
|
|
for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) {
|
|
const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode);
|
|
for (const RRNodeId& track_node : track2track_map[gsb_side][inode]) {
|
|
rr_graph.create_edge(chan_node, track_node, rr_node_driver_switches[track_node]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Build track2ipin_map for an IPIN
|
|
* 1. build a list of routing tracks which are allowed for connections
|
|
* We will check the Connection Block (CB) population of each routing track.
|
|
* By comparing current chan_y - ylow, we can determine if a CB connection
|
|
* is required for each routing track
|
|
* 2. Divide the routing tracks by segment types, so that we can balance
|
|
* the connections between IPINs and different types of routing tracks.
|
|
* 3. Scale the Fc of each pin to the actual number of routing tracks
|
|
* actual_Fc = (int) Fc * num_tracks / chan_width
|
|
***********************************************************************/
|
|
static
|
|
void build_gsb_one_ipin_track2pin_map(const RRGraph& rr_graph,
|
|
const RRGSB& rr_gsb,
|
|
const enum e_side& ipin_side,
|
|
const size_t& ipin_node_id,
|
|
const std::vector<int>& Fc,
|
|
const size_t& offset,
|
|
const std::vector<t_segment_inf>& segment_inf,
|
|
t_track2pin_map& track2ipin_map) {
|
|
/* Get a list of segment_ids*/
|
|
enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side);
|
|
SideManager chan_side_manager(chan_side);
|
|
std::vector<RRSegmentId> seg_list = rr_gsb.get_chan_segment_ids(chan_side);
|
|
size_t chan_width = rr_gsb.get_chan_width(chan_side);
|
|
SideManager ipin_side_manager(ipin_side);
|
|
const RRNodeId& ipin_node = rr_gsb.get_ipin_node(ipin_side, ipin_node_id);
|
|
|
|
for (size_t iseg = 0; iseg < seg_list.size(); ++iseg) {
|
|
/* Get a list of node that have the segment id */
|
|
std::vector<size_t> track_list = rr_gsb.get_chan_node_ids_by_segment_ids(chan_side, seg_list[iseg]);
|
|
/* Refine the track_list: keep those will have connection blocks in the GSB */
|
|
std::vector<size_t> actual_track_list;
|
|
for (size_t inode = 0; inode < track_list.size(); ++inode) {
|
|
/* Check if tracks allow connection blocks in the GSB*/
|
|
if (false == is_gsb_in_track_cb_population(rr_graph, rr_gsb, chan_side, track_list[inode], segment_inf)) {
|
|
continue; /* Bypass condition */
|
|
}
|
|
/* Push the node to actual_track_list */
|
|
actual_track_list.push_back(track_list[inode]);
|
|
}
|
|
/* Check the actual track list */
|
|
VTR_ASSERT(0 == actual_track_list.size() % 2);
|
|
|
|
/* Scale Fc */
|
|
int actual_Fc = std::ceil((float)Fc[iseg] * (float)actual_track_list.size() / (float)chan_width);
|
|
/* Minimum Fc should be 2 : ensure we will connect to a pair of routing tracks */
|
|
actual_Fc = std::max(1, actual_Fc);
|
|
/* Compute the step between two connection from this IPIN to tracks:
|
|
* step = W' / Fc', W' and Fc' are the adapted W and Fc from actual_track_list and Fc_in
|
|
*/
|
|
size_t track_step = std::floor((float)actual_track_list.size() / (float)actual_Fc);
|
|
/* Make sure step should be at least 2 */
|
|
track_step = std::max(1, (int)track_step);
|
|
/* Adapt offset to the range of actual_track_list */
|
|
size_t actual_offset = offset % actual_track_list.size();
|
|
/* rotate the track list by an offset */
|
|
if (0 < actual_offset) {
|
|
std::rotate(actual_track_list.begin(), actual_track_list.begin() + actual_offset, actual_track_list.end());
|
|
}
|
|
|
|
/* Assign tracks: since we assign 2 track per round, we increment itrack by 2* step */
|
|
int track_cnt = 0;
|
|
/* Keep assigning until we meet the Fc requirement */
|
|
for (size_t itrack = 0; itrack < actual_track_list.size(); itrack = itrack + 2 * track_step) {
|
|
/* Update pin2track map */
|
|
size_t chan_side_index = chan_side_manager.to_size_t();
|
|
/* itrack may exceed the size of actual_track_list, adapt it */
|
|
size_t actual_itrack = itrack % actual_track_list.size();
|
|
/* track_index may exceed the chan_width(), adapt it */
|
|
size_t track_index = actual_track_list[actual_itrack] % chan_width;
|
|
|
|
track2ipin_map[chan_side_index][track_index].push_back(ipin_node);
|
|
|
|
/* track_index may exceed the chan_width(), adapt it */
|
|
track_index = (actual_track_list[actual_itrack] + 1) % chan_width;
|
|
|
|
track2ipin_map[chan_side_index][track_index].push_back(ipin_node);
|
|
|
|
track_cnt += 2;
|
|
}
|
|
|
|
/* Ensure the number of tracks is similar to Fc */
|
|
/* Give a warning if Fc is < track_cnt */
|
|
/*
|
|
if (actual_Fc != track_cnt) {
|
|
vpr_printf(TIO_MESSAGE_INFO,
|
|
"IPIN Node(%lu) will have a different Fc(=%lu) than specified(=%lu)!\n",
|
|
ipin_node - rr_graph->rr_node, track_cnt, actual_Fc);
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Build opin2track_map for an OPIN
|
|
* 1. build a list of routing tracks which are allowed for connections
|
|
* We will check the Switch Block (SB) population of each routing track.
|
|
* By comparing current chan_y - ylow, we can determine if a SB connection
|
|
* is required for each routing track
|
|
* 2. Divide the routing tracks by segment types, so that we can balance
|
|
* the connections between OPINs and different types of routing tracks.
|
|
* 3. Scale the Fc of each pin to the actual number of routing tracks
|
|
* actual_Fc = (int) Fc * num_tracks / chan_width
|
|
***********************************************************************/
|
|
static
|
|
void build_gsb_one_opin_pin2track_map(const RRGraph& rr_graph,
|
|
const RRGSB& rr_gsb,
|
|
const enum e_side& opin_side,
|
|
const size_t& opin_node_id,
|
|
const std::vector<int>& Fc,
|
|
const size_t& offset,
|
|
const std::vector<t_segment_inf>& segment_inf,
|
|
t_pin2track_map& opin2track_map) {
|
|
/* Get a list of segment_ids*/
|
|
std::vector<RRSegmentId> seg_list = rr_gsb.get_chan_segment_ids(opin_side);
|
|
enum e_side chan_side = opin_side;
|
|
size_t chan_width = rr_gsb.get_chan_width(chan_side);
|
|
SideManager opin_side_manager(opin_side);
|
|
|
|
for (size_t iseg = 0; iseg < seg_list.size(); ++iseg) {
|
|
/* Get a list of node that have the segment id */
|
|
std::vector<size_t> track_list = rr_gsb.get_chan_node_ids_by_segment_ids(chan_side, seg_list[iseg]);
|
|
/* Refine the track_list: keep those will have connection blocks in the GSB */
|
|
std::vector<size_t> actual_track_list;
|
|
for (size_t inode = 0; inode < track_list.size(); ++inode) {
|
|
/* Check if tracks allow connection blocks in the GSB*/
|
|
if (false == is_gsb_in_track_sb_population(rr_graph, rr_gsb, chan_side,
|
|
track_list[inode], segment_inf)) {
|
|
continue; /* Bypass condition */
|
|
}
|
|
if (TRACK_START != determine_track_status_of_gsb(rr_graph, rr_gsb, chan_side, track_list[inode])) {
|
|
continue; /* Bypass condition */
|
|
}
|
|
/* Push the node to actual_track_list */
|
|
actual_track_list.push_back(track_list[inode]);
|
|
}
|
|
|
|
/* Go the next segment if offset is zero or actual_track_list is empty */
|
|
if (0 == actual_track_list.size()) {
|
|
continue;
|
|
}
|
|
|
|
/* Scale Fc */
|
|
int actual_Fc = std::ceil((float)Fc[iseg] * (float)actual_track_list.size() / (float)chan_width);
|
|
/* Minimum Fc should be 1 : ensure we will drive 1 routing track */
|
|
actual_Fc = std::max(1, actual_Fc);
|
|
/* Compute the step between two connection from this IPIN to tracks:
|
|
* step = W' / Fc', W' and Fc' are the adapted W and Fc from actual_track_list and Fc_in
|
|
*/
|
|
size_t track_step = std::floor((float)actual_track_list.size() / (float)actual_Fc);
|
|
/* Track step mush be a multiple of 2!!!*/
|
|
/* Make sure step should be at least 1 */
|
|
track_step = std::max(1, (int)track_step);
|
|
/* Adapt offset to the range of actual_track_list */
|
|
size_t actual_offset = offset % actual_track_list.size();
|
|
|
|
/* No need to rotate if offset is zero */
|
|
if (0 < actual_offset) {
|
|
/* rotate the track list by an offset */
|
|
std::rotate(actual_track_list.begin(), actual_track_list.begin() + actual_offset, actual_track_list.end());
|
|
}
|
|
|
|
/* Assign tracks */
|
|
int track_cnt = 0;
|
|
/* Keep assigning until we meet the Fc requirement */
|
|
for (size_t itrack = 0; itrack < actual_track_list.size(); itrack = itrack + track_step) {
|
|
/* Update pin2track map */
|
|
size_t opin_side_index = opin_side_manager.to_size_t();
|
|
/* itrack may exceed the size of actual_track_list, adapt it */
|
|
size_t actual_itrack = itrack % actual_track_list.size();
|
|
size_t track_index = actual_track_list[actual_itrack];
|
|
const RRNodeId& track_rr_node_index = rr_gsb.get_chan_node(chan_side, track_index);
|
|
opin2track_map[opin_side_index][opin_node_id].push_back(track_rr_node_index);
|
|
/* update track counter */
|
|
track_cnt++;
|
|
/* Stop when we have enough Fc: this may lead to some tracks have zero drivers.
|
|
* So I comment it. And we just make sure its track_cnt >= actual_Fc
|
|
if (actual_Fc == track_cnt) {
|
|
break;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/* Ensure the number of tracks is similar to Fc */
|
|
/* Give a warning if Fc is < track_cnt */
|
|
/*
|
|
if (actual_Fc != track_cnt) {
|
|
vpr_printf(TIO_MESSAGE_INFO,
|
|
"OPIN Node(%lu) will have a different Fc(=%lu) than specified(=%lu)!\n",
|
|
opin_node_id, track_cnt, actual_Fc);
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* Build the track_to_ipin_map[gsb_side][0..chan_width-1][ipin_indices]
|
|
* based on the existing routing resources in the General Switch Block (GSB)
|
|
* This function supports both X-directional and Y-directional tracks
|
|
* The mapping is done in the following steps:
|
|
* 1. Build ipin_to_track_map[gsb_side][0..num_ipin_nodes-1][track_indices]
|
|
* For each IPIN, we ensure at least one connection to the tracks.
|
|
* Then, we assign IPINs to tracks evenly while satisfying the actual_Fc
|
|
* 2. Convert the ipin_to_track_map to track_to_ipin_map
|
|
***********************************************************************/
|
|
t_track2pin_map build_gsb_track_to_ipin_map(const RRGraph& rr_graph,
|
|
const RRGSB& rr_gsb,
|
|
const DeviceGrid& grids,
|
|
const std::vector<t_segment_inf>& segment_inf,
|
|
const std::vector<vtr::Matrix<int>>& Fc_in) {
|
|
t_track2pin_map track2ipin_map;
|
|
/* Resize the matrix */
|
|
track2ipin_map.resize(rr_gsb.get_num_sides());
|
|
|
|
/* offset counter: it aims to balance the track-to-IPIN for each connection block */
|
|
size_t offset_size = 0;
|
|
std::vector<size_t> offset;
|
|
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
|
SideManager side_manager(side);
|
|
enum e_side ipin_side = side_manager.get_side();
|
|
/* Get the chan_side */
|
|
enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side);
|
|
SideManager chan_side_manager(chan_side);
|
|
/* resize offset to the maximum chan_side*/
|
|
offset_size = std::max(offset_size, chan_side_manager.to_size_t() + 1);
|
|
}
|
|
/* Initial offset */
|
|
offset.resize(offset_size);
|
|
offset.assign(offset.size(), 0);
|
|
|
|
/* Walk through each side */
|
|
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
|
SideManager side_manager(side);
|
|
enum e_side ipin_side = side_manager.get_side();
|
|
/* Get the chan_side */
|
|
enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side);
|
|
SideManager chan_side_manager(chan_side);
|
|
/* This track2pin mapping is for Connection Blocks, so we only care two sides! */
|
|
/* Get channel width and resize the matrix */
|
|
size_t chan_width = rr_gsb.get_chan_width(chan_side);
|
|
track2ipin_map[chan_side_manager.to_size_t()].resize(chan_width);
|
|
/* Find the ipin/opin nodes */
|
|
for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(ipin_side); ++inode) {
|
|
const RRNodeId& ipin_node = rr_gsb.get_ipin_node(ipin_side, inode);
|
|
/* Skip EMPTY type */
|
|
if (true == is_empty_type(grids[rr_graph.node_xlow(ipin_node)][rr_graph.node_ylow(ipin_node)].type)) {
|
|
continue;
|
|
}
|
|
|
|
int grid_type_index = grids[rr_graph.node_xlow(ipin_node)][rr_graph.node_ylow(ipin_node)].type->index;
|
|
/* Get Fc of the ipin */
|
|
/* skip Fc = 0 or unintialized, those pins are in the <directlist> */
|
|
bool skip_conn2track = true;
|
|
std::vector<int> ipin_Fc_out;
|
|
for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) {
|
|
int ipin_Fc = Fc_in[grid_type_index][rr_graph.node_pin_num(ipin_node)][iseg];
|
|
ipin_Fc_out.push_back(ipin_Fc);
|
|
if (0 != ipin_Fc) {
|
|
skip_conn2track = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (true == skip_conn2track) {
|
|
continue;
|
|
}
|
|
|
|
VTR_ASSERT(ipin_Fc_out.size() == segment_inf.size());
|
|
|
|
/* Build track2ipin_map for this IPIN */
|
|
build_gsb_one_ipin_track2pin_map(rr_graph, rr_gsb, ipin_side, inode, ipin_Fc_out,
|
|
/* Give an offset for the first track that this ipin will connect to */
|
|
offset[chan_side_manager.to_size_t()],
|
|
segment_inf, track2ipin_map);
|
|
/* update offset */
|
|
offset[chan_side_manager.to_size_t()] += 2;
|
|
//printf("offset[%lu]=%lu\n", chan_side_manager.to_size_t(), offset[chan_side_manager.to_size_t()]);
|
|
}
|
|
}
|
|
|
|
return track2ipin_map;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Build the opin_to_track_map[gsb_side][0..num_opin_nodes-1][track_indices]
|
|
* based on the existing routing resources in the General Switch Block (GSB)
|
|
* This function supports both X-directional and Y-directional tracks
|
|
* The mapping is done in the following steps:
|
|
* 1. Build a list of routing tracks whose starting points locate at this GSB
|
|
* (xlow - gsb_x == 0)
|
|
* 2. Divide the routing tracks by segment types, so that we can balance
|
|
* the connections between OPINs and different types of routing tracks.
|
|
* 3. Scale the Fc of each pin to the actual number of routing tracks
|
|
* actual_Fc = (int) Fc * num_tracks / chan_width
|
|
***********************************************************************/
|
|
t_pin2track_map build_gsb_opin_to_track_map(const RRGraph& rr_graph,
|
|
const RRGSB& rr_gsb,
|
|
const DeviceGrid& grids,
|
|
const std::vector<t_segment_inf>& segment_inf,
|
|
const std::vector<vtr::Matrix<int>>& Fc_out) {
|
|
t_pin2track_map opin2track_map;
|
|
/* Resize the matrix */
|
|
opin2track_map.resize(rr_gsb.get_num_sides());
|
|
|
|
/* offset counter: it aims to balance the OPIN-to-track for each switch block */
|
|
std::vector<size_t> offset;
|
|
/* Get the chan_side: which is the same as the opin side */
|
|
offset.resize(rr_gsb.get_num_sides());
|
|
/* Initial offset */
|
|
offset.assign(offset.size(), 0);
|
|
|
|
/* Walk through each side */
|
|
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
|
SideManager side_manager(side);
|
|
enum e_side opin_side = side_manager.get_side();
|
|
/* Get the chan_side */
|
|
/* This track2pin mapping is for Connection Blocks, so we only care two sides! */
|
|
/* Get channel width and resize the matrix */
|
|
size_t num_opin_nodes = rr_gsb.get_num_opin_nodes(opin_side);
|
|
opin2track_map[side].resize(num_opin_nodes);
|
|
/* Find the ipin/opin nodes */
|
|
for (size_t inode = 0; inode < num_opin_nodes; ++inode) {
|
|
const RRNodeId& opin_node = rr_gsb.get_opin_node(opin_side, inode);
|
|
/* Skip EMPTY type */
|
|
if (true == is_empty_type(grids[rr_graph.node_xlow(opin_node)][rr_graph.node_ylow(opin_node)].type)) {
|
|
continue;
|
|
}
|
|
int grid_type_index = grids[rr_graph.node_xlow(opin_node)][rr_graph.node_ylow(opin_node)].type->index;
|
|
|
|
/* Get Fc of the ipin */
|
|
/* skip Fc = 0 or unintialized, those pins are in the <directlist> */
|
|
bool skip_conn2track = true;
|
|
std::vector<int> opin_Fc_out;
|
|
for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) {
|
|
int opin_Fc = Fc_out[grid_type_index][rr_graph.node_pin_num(opin_node)][iseg];
|
|
opin_Fc_out.push_back(opin_Fc);
|
|
if (0 != opin_Fc) {
|
|
skip_conn2track = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (true == skip_conn2track) {
|
|
continue;
|
|
}
|
|
VTR_ASSERT(opin_Fc_out.size() == segment_inf.size());
|
|
|
|
/* Build track2ipin_map for this IPIN */
|
|
build_gsb_one_opin_pin2track_map(rr_graph, rr_gsb, opin_side, inode, opin_Fc_out,
|
|
/* Give an offset for the first track that this ipin will connect to */
|
|
offset[side_manager.to_size_t()],
|
|
segment_inf, opin2track_map);
|
|
/* update offset: aim to rotate starting tracks by 1*/
|
|
offset[side_manager.to_size_t()] += 1;
|
|
}
|
|
|
|
/* Check:
|
|
* 1. We want to ensure that each OPIN will drive at least one track
|
|
* 2. We want to ensure that each track will be driven by at least 1 OPIN */
|
|
}
|
|
|
|
return opin2track_map;
|
|
}
|
|
|
|
/************************************************************************
|
|
* Add all direct clb-pin-to-clb-pin edges to given opin
|
|
***********************************************************************/
|
|
void build_direct_connections_for_one_gsb(RRGraph& rr_graph,
|
|
const DeviceGrid& grids,
|
|
const vtr::Point<size_t>& from_grid_coordinate,
|
|
const RRSwitchId& delayless_switch,
|
|
const std::vector<t_direct_inf>& directs,
|
|
const std::vector<t_clb_to_clb_directs>& clb_to_clb_directs) {
|
|
VTR_ASSERT(directs.size() == clb_to_clb_directs.size());
|
|
|
|
const t_grid_tile& from_grid = grids[from_grid_coordinate.x()][from_grid_coordinate.y()];
|
|
t_physical_tile_type_ptr grid_type = from_grid.type;
|
|
|
|
/* Iterate through all direct connections */
|
|
for (size_t i = 0; i < directs.size(); ++i) {
|
|
/* Bypass unmatched direct clb-to-clb connections */
|
|
if (grid_type != clb_to_clb_directs[i].from_clb_type) {
|
|
continue;
|
|
}
|
|
|
|
/* This opin is specified to connect directly to an ipin,
|
|
* now compute which ipin to connect to
|
|
*/
|
|
vtr::Point<size_t> to_grid_coordinate(from_grid_coordinate.x() + directs[i].x_offset,
|
|
from_grid_coordinate.y() + directs[i].y_offset);
|
|
|
|
/* Bypass unmatched direct clb-to-clb connections */
|
|
t_physical_tile_type_ptr to_grid_type = grids[to_grid_coordinate.x()][to_grid_coordinate.y()].type;
|
|
/* Check if to_grid if the same grid */
|
|
if (to_grid_type != clb_to_clb_directs[i].to_clb_type) {
|
|
continue;
|
|
}
|
|
|
|
bool swap;
|
|
int max_index, min_index;
|
|
/* Compute index of opin with regards to given pins */
|
|
if ( clb_to_clb_directs[i].from_clb_pin_start_index
|
|
> clb_to_clb_directs[i].from_clb_pin_end_index) {
|
|
swap = true;
|
|
max_index = clb_to_clb_directs[i].from_clb_pin_start_index;
|
|
min_index = clb_to_clb_directs[i].from_clb_pin_end_index;
|
|
} else {
|
|
swap = false;
|
|
min_index = clb_to_clb_directs[i].from_clb_pin_start_index;
|
|
max_index = clb_to_clb_directs[i].from_clb_pin_end_index;
|
|
}
|
|
|
|
/* get every opin in the range */
|
|
for (int opin = min_index; opin <= max_index; ++opin) {
|
|
int offset = opin - min_index;
|
|
|
|
if ( (to_grid_coordinate.x() < grids.width() - 1)
|
|
&& (to_grid_coordinate.y() < grids.height() - 1) ) {
|
|
int ipin = OPEN;
|
|
if ( clb_to_clb_directs[i].to_clb_pin_start_index
|
|
> clb_to_clb_directs[i].to_clb_pin_end_index) {
|
|
if (true == swap) {
|
|
ipin = clb_to_clb_directs[i].to_clb_pin_end_index + offset;
|
|
} else {
|
|
ipin = clb_to_clb_directs[i].to_clb_pin_start_index - offset;
|
|
}
|
|
} else {
|
|
if(true == swap) {
|
|
ipin = clb_to_clb_directs[i].to_clb_pin_end_index - offset;
|
|
} else {
|
|
ipin = clb_to_clb_directs[i].to_clb_pin_start_index + offset;
|
|
}
|
|
}
|
|
|
|
/* Get the pin index in the rr_graph */
|
|
int from_grid_width_ofs = from_grid.width_offset;
|
|
int from_grid_height_ofs = from_grid.height_offset;
|
|
int to_grid_width_ofs = grids[to_grid_coordinate.x()][to_grid_coordinate.y()].width_offset;
|
|
int to_grid_height_ofs = grids[to_grid_coordinate.x()][to_grid_coordinate.y()].height_offset;
|
|
|
|
/* Find the side of grid pins, the pin location should be unique!
|
|
* Pin location is required by searching a node in rr_graph
|
|
*/
|
|
std::vector<e_side> opin_grid_side = find_grid_pin_sides(from_grid, opin);
|
|
VTR_ASSERT(1 == opin_grid_side.size());
|
|
|
|
std::vector<e_side> ipin_grid_side = find_grid_pin_sides(grids[to_grid_coordinate.x()][to_grid_coordinate.y()], ipin);
|
|
VTR_ASSERT(1 == ipin_grid_side.size());
|
|
|
|
const RRNodeId& opin_node_id = rr_graph.find_node(from_grid_coordinate.x() - from_grid_width_ofs,
|
|
from_grid_coordinate.y() - from_grid_height_ofs,
|
|
OPIN, opin, opin_grid_side[0]);
|
|
const RRNodeId& ipin_node_id = rr_graph.find_node(to_grid_coordinate.x() - to_grid_width_ofs,
|
|
to_grid_coordinate.y() - to_grid_height_ofs,
|
|
IPIN, ipin, ipin_grid_side[0]);
|
|
/*
|
|
VTR_LOG("Direct connection: from grid[%lu][%lu].pin[%lu] at side %s to grid[%lu][%lu].pin[%lu] at side %s\n",
|
|
from_grid_coordinate.x() - from_grid_width_ofs,
|
|
from_grid_coordinate.y() - from_grid_height_ofs,
|
|
opin, SIDE_STRING[opin_grid_side[0]],
|
|
to_grid_coordinate.x() - to_grid_width_ofs,
|
|
to_grid_coordinate.y() - to_grid_height_ofs,
|
|
ipin, SIDE_STRING[ipin_grid_side[0]]);
|
|
*/
|
|
|
|
/* add edges to the opin_node */
|
|
rr_graph.create_edge(opin_node_id, ipin_node_id,
|
|
delayless_switch);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} /* end namespace openfpga */
|