/************************************************************************ * This file contains a builder for track-to-track connections inside a * tileable General Switch Block (GSB). ***********************************************************************/ #include #include #include /* 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> 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 side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); /* Get the coordinate of where the track starts */ vtr::Point 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 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& segment_inf) { /* Get the rr_node */ RRNodeId track_node = rr_gsb.get_chan_node(gsb_side, track_id); /* Get the coordinates */ vtr::Point side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); vtr::Point 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& segment_inf) { /* Get the rr_node */ const RRNodeId& track_node = rr_gsb.get_chan_node(gsb_side, track_id); /* Get the coordinates */ vtr::Point side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); vtr::Point 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 get_to_track_list(const int& Fs, const int& to_track, const int& num_to_tracks) { std::vector 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 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 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 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 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::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& 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& chan_coordinate, const t_rr_type& chan_type, const RRGraph& rr_graph, const ChanNodeDetails& chan_details) { std::vector 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& device_chan_width, const std::vector& segment_inf, const vtr::Point& 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 coordinate = rr_gsb.get_side_block_coordinate(side_manager.get_side()); RRChan rr_chan; std::vector> 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 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 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& 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& Fc, const size_t& offset, const std::vector& 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 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 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 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& Fc, const size_t& offset, const std::vector& segment_inf, t_pin2track_map& opin2track_map) { /* Get a list of segment_ids*/ std::vector 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 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 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& segment_inf, const std::vector>& 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 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 */ bool skip_conn2track = true; std::vector 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& segment_inf, const std::vector>& 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 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 */ bool skip_conn2track = true; std::vector 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& from_grid_coordinate, const RRSwitchId& delayless_switch, const std::vector& directs, const std::vector& 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 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 opin_grid_side = find_grid_pin_sides(from_grid, opin); VTR_ASSERT(1 == opin_grid_side.size()); std::vector 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 */