| #include <cstring> |
| #include <unordered_set> |
| #include <regex> |
| #include <algorithm> |
| |
| #include "vtr_assert.h" |
| #include "vtr_log.h" |
| #include "vtr_memory.h" |
| |
| #include "vpr_types.h" |
| #include "vpr_error.h" |
| |
| #include "physical_types.h" |
| #include "globals.h" |
| #include "vpr_utils.h" |
| #include "cluster_placement.h" |
| #include "place_macro.h" |
| #include "string.h" |
| #include "pack_types.h" |
| #include "device_grid.h" |
| |
| /* This module contains subroutines that are used in several unrelated parts * |
| * of VPR. They are VPR-specific utility routines. */ |
| |
| /* This defines the maximum string length that could be parsed by functions * |
| * in vpr_utils. */ |
| #define MAX_STRING_LEN 128 |
| |
| /******************** File-scope variables declarations **********************/ |
| |
| /* These three mappings are needed since there are two different netlist * |
| * conventions - in the cluster level, ports and port pins are used * |
| * while in the post-pack level, block pins are used. The reason block * |
| * type is used instead of blocks is to save memories. */ |
| |
| /* f_port_from_blk_pin array allow us to quickly find what port a block * |
| * pin corresponds to. * |
| * [0...device_ctx.logical_block_type.size()-1][0...blk_pin_count-1] * |
| * */ |
| static int** f_port_from_blk_pin = nullptr; |
| |
| /* f_port_pin_from_blk_pin array allow us to quickly find what port pin a* |
| * block pin corresponds to. * |
| * [0...device_ctx.logical_block_types.size()-1][0...blk_pin_count-1] */ |
| static int** f_port_pin_from_blk_pin = nullptr; |
| |
| /* f_port_pin_to_block_pin array allows us to quickly find what block * |
| * pin a port pin corresponds to. * |
| * [0...device_ctx.logical_block_types.size()-1][0...num_ports-1][0...num_port_pins-1] */ |
| static int*** f_blk_pin_from_port_pin = nullptr; |
| |
| //Regular expressions used to determine register and logic primitives |
| //TODO: Make this set-able from command-line? |
| const std::regex REGISTER_MODEL_REGEX("(.subckt\\s+)?.*(latch|dff).*", std::regex::icase); |
| const std::regex LOGIC_MODEL_REGEX("(.subckt\\s+)?.*(lut|names|lcell).*", std::regex::icase); |
| |
| /******************** Subroutine declarations ********************************/ |
| |
| /* Allocates and loads f_port_from_blk_pin and f_port_pin_from_blk_pin * |
| * arrays. * |
| * The arrays are freed in free_placement_structs() */ |
| static void alloc_and_load_port_pin_from_blk_pin(); |
| |
| /* Allocates and loads blk_pin_from_port_pin array. * |
| * The arrays are freed in free_placement_structs() */ |
| static void alloc_and_load_blk_pin_from_port_pin(); |
| |
| /* Go through all the ports in all the blocks to find the port that has the same * |
| * name as port_name and belongs to the block type that has the name pb_type_name. * |
| * Then, check that whether start_pin_index and end_pin_index are specified. If * |
| * they are, mark down the pins from start_pin_index to end_pin_index, inclusive. * |
| * Otherwise, mark down all the pins in that port. */ |
| static void mark_direct_of_ports(int idirect, int direct_type, char* pb_type_name, char* port_name, int end_pin_index, int start_pin_index, char* src_string, int line, int** idirect_from_blk_pin, int** direct_type_from_blk_pin); |
| |
| /* Mark the pin entry in idirect_from_blk_pin with idirect and the pin entry in * |
| * direct_type_from_blk_pin with direct_type from start_pin_index to * |
| * end_pin_index. */ |
| static void mark_direct_of_pins(int start_pin_index, int end_pin_index, int itype, int iport, int** idirect_from_blk_pin, int idirect, int** direct_type_from_blk_pin, int direct_type, int line, char* src_string); |
| |
| static void load_pb_graph_pin_lookup_from_index_rec(t_pb_graph_pin** pb_graph_pin_lookup_from_index, t_pb_graph_node* pb_graph_node); |
| |
| static void load_pin_id_to_pb_mapping_rec(t_pb* cur_pb, t_pb** pin_id_to_pb_mapping); |
| |
| static std::vector<int> find_connected_internal_clb_sink_pins(ClusterBlockId clb, int pb_pin); |
| static void find_connected_internal_clb_sink_pins_recurr(ClusterBlockId clb, int pb_pin, std::vector<int>& connected_sink_pb_pins); |
| static AtomPinId find_atom_pin_for_pb_route_id(ClusterBlockId clb, int pb_route_id, const IntraLbPbPinLookup& pb_gpin_lookup); |
| |
| static bool block_type_contains_blif_model(t_logical_block_type_ptr type, const std::regex& blif_model_regex); |
| static bool pb_type_contains_blif_model(const t_pb_type* pb_type, const std::regex& blif_model_regex); |
| |
| /******************** Subroutine definitions *********************************/ |
| |
| const t_model* find_model(const t_model* models, const std::string& name, bool required) { |
| for (const t_model* model = models; model != nullptr; model = model->next) { |
| if (name == model->name) { |
| return model; |
| } |
| } |
| |
| if (required) { |
| VPR_FATAL_ERROR(VPR_ERROR_ARCH, "Failed to find architecture modedl '%s'\n", name.c_str()); |
| } |
| |
| return nullptr; |
| } |
| |
| const t_model_ports* find_model_port(const t_model* model, const std::string& name, bool required) { |
| VTR_ASSERT(model); |
| |
| for (const t_model_ports* model_ports : {model->inputs, model->outputs}) { |
| for (const t_model_ports* port = model_ports; port != nullptr; port = port->next) { |
| if (port->name == name) { |
| return port; |
| } |
| } |
| } |
| |
| if (required) { |
| VPR_FATAL_ERROR(VPR_ERROR_ARCH, "Failed to find port '%s; on architecture modedl '%s'\n", name.c_str(), model->name); |
| } |
| |
| return nullptr; |
| } |
| |
| /** |
| * print tabs given number of tabs to file |
| */ |
| void print_tabs(FILE* fpout, int num_tab) { |
| int i; |
| for (i = 0; i < num_tab; i++) { |
| fprintf(fpout, "\t"); |
| } |
| } |
| |
| /* Points the place_ctx.grid_blocks structure back to the blocks list */ |
| void sync_grid_to_blocks() { |
| auto& place_ctx = g_vpr_ctx.mutable_placement(); |
| auto& device_ctx = g_vpr_ctx.device(); |
| auto& device_grid = device_ctx.grid; |
| |
| /* Reset usage and allocate blocks list if needed */ |
| auto& grid_blocks = place_ctx.grid_blocks; |
| grid_blocks.resize({device_grid.width(), device_grid.height()}); |
| for (size_t x = 0; x < device_grid.width(); ++x) { |
| for (size_t y = 0; y < device_grid.height(); ++y) { |
| auto& grid_block = grid_blocks[x][y]; |
| grid_block.blocks.resize(device_ctx.grid[x][y].type->capacity); |
| |
| for (int z = 0; z < device_ctx.grid[x][y].type->capacity; ++z) { |
| grid_block.blocks[z] = EMPTY_BLOCK_ID; |
| } |
| } |
| } |
| |
| /* Go through each block */ |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| for (auto blk_id : cluster_ctx.clb_nlist.blocks()) { |
| int blk_x = place_ctx.block_locs[blk_id].loc.x; |
| int blk_y = place_ctx.block_locs[blk_id].loc.y; |
| int blk_z = place_ctx.block_locs[blk_id].loc.z; |
| |
| auto type = physical_tile_type(blk_id); |
| |
| /* Check range of block coords */ |
| if (blk_x < 0 || blk_y < 0 |
| || (blk_x + type->width - 1) > int(device_ctx.grid.width() - 1) |
| || (blk_y + type->height - 1) > int(device_ctx.grid.height() - 1) |
| || blk_z < 0 || blk_z > (type->capacity)) { |
| VPR_FATAL_ERROR(VPR_ERROR_PLACE, "Block %zu is at invalid location (%d, %d, %d).\n", |
| size_t(blk_id), blk_x, blk_y, blk_z); |
| } |
| |
| /* Check types match */ |
| if (type != device_ctx.grid[blk_x][blk_y].type) { |
| VPR_FATAL_ERROR(VPR_ERROR_PLACE, "A block is in a grid location (%d x %d) with a conflicting types '%s' and '%s' .\n", |
| blk_x, blk_y, |
| type->name, |
| device_ctx.grid[blk_x][blk_y].type->name); |
| } |
| |
| /* Check already in use */ |
| if ((EMPTY_BLOCK_ID != place_ctx.grid_blocks[blk_x][blk_y].blocks[blk_z]) |
| && (INVALID_BLOCK_ID != place_ctx.grid_blocks[blk_x][blk_y].blocks[blk_z])) { |
| VPR_FATAL_ERROR(VPR_ERROR_PLACE, "Location (%d, %d, %d) is used more than once.\n", |
| blk_x, blk_y, blk_z); |
| } |
| |
| if (device_ctx.grid[blk_x][blk_y].width_offset != 0 || device_ctx.grid[blk_x][blk_y].height_offset != 0) { |
| VPR_FATAL_ERROR(VPR_ERROR_PLACE, "Large block not aligned in placment for cluster_ctx.blocks %lu at (%d, %d, %d).", |
| size_t(blk_id), blk_x, blk_y, blk_z); |
| } |
| |
| /* Set the block */ |
| for (int width = 0; width < type->width; ++width) { |
| for (int height = 0; height < type->height; ++height) { |
| place_ctx.grid_blocks[blk_x + width][blk_y + height].blocks[blk_z] = blk_id; |
| place_ctx.grid_blocks[blk_x + width][blk_y + height].usage++; |
| VTR_ASSERT(device_ctx.grid[blk_x + width][blk_y + height].width_offset == width); |
| VTR_ASSERT(device_ctx.grid[blk_x + width][blk_y + height].height_offset == height); |
| } |
| } |
| } |
| } |
| |
| std::string block_type_pin_index_to_name(t_physical_tile_type_ptr type, int pin_index) { |
| VTR_ASSERT(pin_index < type->num_pins); |
| |
| std::string pin_name = type->name; |
| |
| if (type->capacity > 1) { |
| int pins_per_inst = type->num_pins / type->capacity; |
| int inst_num = pin_index / pins_per_inst; |
| pin_index %= pins_per_inst; |
| |
| pin_name += "[" + std::to_string(inst_num) + "]"; |
| } |
| |
| pin_name += "."; |
| |
| t_pb_type* pb_type = logical_block_type(type)->pb_type; |
| int curr_index = 0; |
| for (int iport = 0; iport < pb_type->num_ports; ++iport) { |
| t_port* port = &pb_type->ports[iport]; |
| |
| if (curr_index + port->num_pins > pin_index) { |
| //This port contains the desired pin index |
| int index_in_port = pin_index - curr_index; |
| pin_name += port->name; |
| pin_name += "[" + std::to_string(index_in_port) + "]"; |
| return pin_name; |
| } |
| |
| curr_index += port->num_pins; |
| } |
| |
| return "<UNKOWN>"; |
| } |
| |
| std::vector<std::string> block_type_class_index_to_pin_names(t_physical_tile_type_ptr type, int class_index) { |
| VTR_ASSERT(class_index < type->num_class); |
| |
| //TODO: unsure if classes are modulo capacity or not... so this may be unnessesary |
| int classes_per_inst = type->num_class / type->capacity; |
| class_index %= classes_per_inst; |
| |
| t_class& class_inf = type->class_inf[class_index]; |
| |
| std::vector<std::string> pin_names; |
| for (int ipin = 0; ipin < class_inf.num_pins; ++ipin) { |
| pin_names.push_back(block_type_pin_index_to_name(type, class_inf.pinlist[ipin])); |
| } |
| |
| return pin_names; |
| } |
| |
| std::string rr_node_arch_name(int inode) { |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| const t_rr_node& rr_node = device_ctx.rr_nodes[inode]; |
| |
| std::string rr_node_arch_name; |
| if (rr_node.type() == OPIN || rr_node.type() == IPIN) { |
| //Pin names |
| auto type = device_ctx.grid[rr_node.xlow()][rr_node.ylow()].type; |
| rr_node_arch_name += block_type_pin_index_to_name(type, rr_node.ptc_num()); |
| } else if (rr_node.type() == SOURCE || rr_node.type() == SINK) { |
| //Set of pins associated with SOURCE/SINK |
| auto type = device_ctx.grid[rr_node.xlow()][rr_node.ylow()].type; |
| auto pin_names = block_type_class_index_to_pin_names(type, rr_node.ptc_num()); |
| if (pin_names.size() > 1) { |
| rr_node_arch_name += rr_node.type_string(); |
| rr_node_arch_name += " connected to "; |
| rr_node_arch_name += "{"; |
| rr_node_arch_name += vtr::join(pin_names, ", "); |
| rr_node_arch_name += "}"; |
| } else { |
| rr_node_arch_name += pin_names[0]; |
| } |
| } else { |
| VTR_ASSERT(rr_node.type() == CHANX || rr_node.type() == CHANY); |
| //Wire segment name |
| auto cost_index = rr_node.cost_index(); |
| int seg_index = device_ctx.rr_indexed_data[cost_index].seg_index; |
| |
| rr_node_arch_name += device_ctx.arch->Segments[seg_index].name; |
| } |
| |
| return rr_node_arch_name; |
| } |
| |
| IntraLbPbPinLookup::IntraLbPbPinLookup(const std::vector<t_logical_block_type>& block_types) |
| : block_types_(block_types) { |
| intra_lb_pb_pin_lookup_ = new t_pb_graph_pin**[block_types_.size()]; |
| for (unsigned int itype = 0; itype < block_types_.size(); ++itype) { |
| intra_lb_pb_pin_lookup_[itype] = alloc_and_load_pb_graph_pin_lookup_from_index(&block_types[itype]); |
| } |
| } |
| |
| IntraLbPbPinLookup::IntraLbPbPinLookup(const IntraLbPbPinLookup& rhs) |
| : IntraLbPbPinLookup(rhs.block_types_) { |
| //nop |
| } |
| |
| IntraLbPbPinLookup& IntraLbPbPinLookup::operator=(IntraLbPbPinLookup rhs) { |
| //Copy-swap idiom |
| swap(*this, rhs); |
| |
| return *this; |
| } |
| |
| IntraLbPbPinLookup::~IntraLbPbPinLookup() { |
| auto& device_ctx = g_vpr_ctx.device(); |
| for (unsigned int itype = 0; itype < device_ctx.logical_block_types.size(); itype++) { |
| free_pb_graph_pin_lookup_from_index(intra_lb_pb_pin_lookup_[itype]); |
| } |
| |
| delete[] intra_lb_pb_pin_lookup_; |
| } |
| |
| const t_pb_graph_pin* IntraLbPbPinLookup::pb_gpin(unsigned int itype, int ipin) const { |
| VTR_ASSERT(itype < block_types_.size()); |
| |
| return intra_lb_pb_pin_lookup_[itype][ipin]; |
| } |
| |
| void swap(IntraLbPbPinLookup& lhs, IntraLbPbPinLookup& rhs) { |
| std::swap(lhs.block_types_, rhs.block_types_); |
| std::swap(lhs.intra_lb_pb_pin_lookup_, rhs.intra_lb_pb_pin_lookup_); |
| } |
| |
| //Returns the set of pins which are connected to the top level clb pin |
| // The pin(s) may be input(s) or and output (returning the connected sinks or drivers respectively) |
| std::vector<AtomPinId> find_clb_pin_connected_atom_pins(ClusterBlockId clb, int clb_pin, const IntraLbPbPinLookup& pb_gpin_lookup) { |
| std::vector<AtomPinId> atom_pins; |
| |
| if (is_opin(clb_pin, physical_tile_type(clb))) { |
| //output |
| AtomPinId driver = find_clb_pin_driver_atom_pin(clb, clb_pin, pb_gpin_lookup); |
| if (driver) { |
| atom_pins.push_back(driver); |
| } |
| } else { |
| //input |
| atom_pins = find_clb_pin_sink_atom_pins(clb, clb_pin, pb_gpin_lookup); |
| } |
| |
| return atom_pins; |
| } |
| |
| //Returns the atom pin which drives the top level clb output pin |
| AtomPinId find_clb_pin_driver_atom_pin(ClusterBlockId clb, int clb_pin, const IntraLbPbPinLookup& pb_gpin_lookup) { |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| auto& atom_ctx = g_vpr_ctx.atom(); |
| |
| int pb_pin_id = find_clb_pb_pin(clb, clb_pin); |
| if (pb_pin_id < 0) { |
| //CLB output pin has no internal driver |
| return AtomPinId::INVALID(); |
| } |
| const t_pb_routes& pb_routes = cluster_ctx.clb_nlist.block_pb(clb)->pb_route; |
| AtomNetId atom_net = pb_routes[pb_pin_id].atom_net_id; |
| |
| //Trace back until the driver is reached |
| while (pb_routes[pb_pin_id].driver_pb_pin_id >= 0) { |
| pb_pin_id = pb_routes[pb_pin_id].driver_pb_pin_id; |
| } |
| VTR_ASSERT(pb_pin_id >= 0); |
| |
| VTR_ASSERT(atom_net == pb_routes[pb_pin_id].atom_net_id); |
| |
| //Map the pb_pin_id to AtomPinId |
| AtomPinId atom_pin = find_atom_pin_for_pb_route_id(clb, pb_pin_id, pb_gpin_lookup); |
| VTR_ASSERT(atom_pin); |
| |
| VTR_ASSERT_MSG(atom_ctx.nlist.pin_net(atom_pin) == atom_net, "Driver atom pin should drive the same net"); |
| |
| return atom_pin; |
| } |
| |
| //Returns the set of atom sink pins associated with the top level clb input pin |
| std::vector<AtomPinId> find_clb_pin_sink_atom_pins(ClusterBlockId clb, int clb_pin, const IntraLbPbPinLookup& pb_gpin_lookup) { |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| auto& atom_ctx = g_vpr_ctx.atom(); |
| |
| const t_pb_routes& pb_routes = cluster_ctx.clb_nlist.block_pb(clb)->pb_route; |
| |
| VTR_ASSERT_MSG(clb_pin < physical_tile_type(clb)->num_pins, "Must be a valid top-level pin"); |
| |
| int pb_pin = find_clb_pb_pin(clb, clb_pin); |
| |
| VTR_ASSERT(cluster_ctx.clb_nlist.block_pb(clb)); |
| VTR_ASSERT_MSG(pb_pin < cluster_ctx.clb_nlist.block_pb(clb)->pb_graph_node->num_pins(), "Pin must map to a top-level pb pin"); |
| |
| VTR_ASSERT_MSG(pb_routes[pb_pin].driver_pb_pin_id < 0, "CLB input pin should have no internal drivers"); |
| |
| AtomNetId atom_net = pb_routes[pb_pin].atom_net_id; |
| VTR_ASSERT(atom_net); |
| |
| std::vector<int> connected_sink_pb_pins = find_connected_internal_clb_sink_pins(clb, pb_pin); |
| |
| std::vector<AtomPinId> sink_atom_pins; |
| for (int sink_pb_pin : connected_sink_pb_pins) { |
| //Map the pb_pin_id to AtomPinId |
| AtomPinId atom_pin = find_atom_pin_for_pb_route_id(clb, sink_pb_pin, pb_gpin_lookup); |
| VTR_ASSERT(atom_pin); |
| |
| VTR_ASSERT_MSG(atom_ctx.nlist.pin_net(atom_pin) == atom_net, "Sink atom pins should be driven by the same net"); |
| |
| sink_atom_pins.push_back(atom_pin); |
| } |
| |
| return sink_atom_pins; |
| } |
| |
| //Find sinks internal to the given clb which are connected to the specified pb_pin |
| static std::vector<int> find_connected_internal_clb_sink_pins(ClusterBlockId clb, int pb_pin) { |
| std::vector<int> connected_sink_pb_pins; |
| find_connected_internal_clb_sink_pins_recurr(clb, pb_pin, connected_sink_pb_pins); |
| |
| return connected_sink_pb_pins; |
| } |
| |
| //Recursive helper for find_connected_internal_clb_sink_pins() |
| static void find_connected_internal_clb_sink_pins_recurr(ClusterBlockId clb, int pb_pin, std::vector<int>& connected_sink_pb_pins) { |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| |
| if (cluster_ctx.clb_nlist.block_pb(clb)->pb_route[pb_pin].sink_pb_pin_ids.empty()) { |
| //No more sinks => primitive input |
| connected_sink_pb_pins.push_back(pb_pin); |
| } |
| for (int sink_pb_pin : cluster_ctx.clb_nlist.block_pb(clb)->pb_route[pb_pin].sink_pb_pin_ids) { |
| find_connected_internal_clb_sink_pins_recurr(clb, sink_pb_pin, connected_sink_pb_pins); |
| } |
| } |
| |
| //Maps from a CLB's pb_route ID to it's matching AtomPinId (if the pb_route is a primitive pin) |
| static AtomPinId find_atom_pin_for_pb_route_id(ClusterBlockId clb, int pb_route_id, const IntraLbPbPinLookup& pb_gpin_lookup) { |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| auto& atom_ctx = g_vpr_ctx.atom(); |
| |
| VTR_ASSERT_MSG(cluster_ctx.clb_nlist.block_pb(clb)->pb_route[pb_route_id].atom_net_id, "PB route should correspond to a valid atom net"); |
| |
| //Find the graph pin associated with this pb_route |
| const t_pb_graph_pin* gpin = pb_gpin_lookup.pb_gpin(cluster_ctx.clb_nlist.block_type(clb)->index, pb_route_id); |
| VTR_ASSERT(gpin); |
| |
| //Get the PB associated with this block |
| const t_pb* pb = cluster_ctx.clb_nlist.block_pb(clb); |
| |
| //Find the graph node containing the pin |
| const t_pb_graph_node* gnode = gpin->parent_node; |
| |
| //Find the pb matching the gnode |
| const t_pb* child_pb = pb->find_pb(gnode); |
| |
| VTR_ASSERT_MSG(child_pb, "Should find pb containing pb route"); |
| |
| //Check if this is a leaf/atom pb |
| if (child_pb->child_pbs == nullptr) { |
| //It is a leaf, and hence should map to an atom |
| |
| //Find the associated atom |
| AtomBlockId atom_block = atom_ctx.lookup.pb_atom(child_pb); |
| VTR_ASSERT(atom_block); |
| |
| //Now find the matching pin by seeing which pin maps to the gpin |
| for (AtomPinId atom_pin : atom_ctx.nlist.block_pins(atom_block)) { |
| const t_pb_graph_pin* atom_pin_gpin = atom_ctx.lookup.atom_pin_pb_graph_pin(atom_pin); |
| if (atom_pin_gpin == gpin) { |
| //Match |
| return atom_pin; |
| } |
| } |
| } |
| |
| //No match |
| return AtomPinId::INVALID(); |
| } |
| |
| /* Return the net pin which drive the CLB input connected to sink_pb_pin_id, or nullptr if none (i.e. driven internally) |
| * clb: Block in which the the sink pin is located on |
| * sink_pb_pin_id: The physical pin index of the sink pin on the block |
| * |
| * Returns a tuple containing |
| * ClusterNetId: Corresponds to the net connected to the sink pin |
| * INVALID if not an external CLB pin, or if it's an output (driver pin) |
| * clb_pin: Physical pin index, same as sink_pb_pin_id but potentially with an offset (if z is defined) |
| * -1 if not an external CLB pin, or if it's an output (driver pin) |
| * clb_net_pin: Index of the pin relative to the net, (i.e. 0 = driver, +1 = sink) |
| * -1 if not an external CLB pin, or if it's an output (driver pin) |
| */ |
| std::tuple<ClusterNetId, int, int> find_pb_route_clb_input_net_pin(ClusterBlockId clb, int sink_pb_pin_id) { |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| |
| VTR_ASSERT(sink_pb_pin_id < cluster_ctx.clb_nlist.block_pb(clb)->pb_graph_node->total_pb_pins); |
| VTR_ASSERT(clb != ClusterBlockId::INVALID()); |
| VTR_ASSERT(sink_pb_pin_id >= 0); |
| |
| const t_pb_routes& pb_routes = cluster_ctx.clb_nlist.block_pb(clb)->pb_route; |
| |
| VTR_ASSERT_MSG(pb_routes[sink_pb_pin_id].atom_net_id, "PB route should be associated with a net"); |
| |
| //Walk back from the sink to the CLB input pin |
| int curr_pb_pin_id = sink_pb_pin_id; |
| int next_pb_pin_id = pb_routes[curr_pb_pin_id].driver_pb_pin_id; |
| while (next_pb_pin_id >= 0) { |
| //Advance back towards the input |
| curr_pb_pin_id = next_pb_pin_id; |
| |
| VTR_ASSERT_MSG(pb_routes[next_pb_pin_id].atom_net_id == pb_routes[sink_pb_pin_id].atom_net_id, |
| "Connected pb_routes should connect the same net"); |
| |
| next_pb_pin_id = pb_routes[curr_pb_pin_id].driver_pb_pin_id; |
| } |
| |
| bool is_output_pin = (pb_routes[curr_pb_pin_id].pb_graph_pin->port->type == OUT_PORT); |
| |
| if (!is_clb_external_pin(clb, curr_pb_pin_id) || is_output_pin) { |
| return std::make_tuple(ClusterNetId::INVALID(), -1, -1); |
| } |
| |
| //To account for capacity > 1 blocks we need to convert the pb_pin to the clb pin |
| int clb_pin = find_pb_pin_clb_pin(clb, curr_pb_pin_id); |
| VTR_ASSERT(clb_pin >= 0); |
| |
| //clb_pin should be a top-level CLB input |
| ClusterNetId clb_net_idx = cluster_ctx.clb_nlist.block_net(clb, clb_pin); |
| int clb_net_pin_idx = cluster_ctx.clb_nlist.block_pin_net_index(clb, clb_pin); |
| VTR_ASSERT(clb_net_idx != ClusterNetId::INVALID()); |
| VTR_ASSERT(clb_net_pin_idx >= 0); |
| |
| return std::tuple<ClusterNetId, int, int>(clb_net_idx, clb_pin, clb_net_pin_idx); |
| } |
| |
| //Return the pb pin index corresponding to the pin clb_pin on block clb |
| // Given a clb_pin index on a this function will return the corresponding |
| // pin index on the pb_type (accounting for the possible z-coordinate offset). |
| int find_clb_pb_pin(ClusterBlockId clb, int clb_pin) { |
| auto& place_ctx = g_vpr_ctx.placement(); |
| |
| auto type = physical_tile_type(clb); |
| VTR_ASSERT_MSG(clb_pin < type->num_pins, "Must be a valid top-level pin"); |
| |
| int pb_pin = -1; |
| if (place_ctx.block_locs[clb].nets_and_pins_synced_to_z_coordinate) { |
| //Pins have been offset by z-coordinate, need to remove offset |
| |
| VTR_ASSERT(type->num_pins % type->capacity == 0); |
| int num_basic_block_pins = type->num_pins / type->capacity; |
| /* Logical location and physical location is offset by z * max_num_block_pins */ |
| |
| pb_pin = clb_pin - place_ctx.block_locs[clb].loc.z * num_basic_block_pins; |
| } else { |
| //No offset |
| pb_pin = clb_pin; |
| } |
| |
| VTR_ASSERT(pb_pin >= 0); |
| |
| return pb_pin; |
| } |
| |
| //Inverse of find_clb_pb_pin() |
| int find_pb_pin_clb_pin(ClusterBlockId clb, int pb_pin) { |
| auto& place_ctx = g_vpr_ctx.placement(); |
| |
| auto type = physical_tile_type(clb); |
| |
| int clb_pin = -1; |
| if (place_ctx.block_locs[clb].nets_and_pins_synced_to_z_coordinate) { |
| //Pins have been offset by z-coordinate, need to remove offset |
| VTR_ASSERT(type->num_pins % type->capacity == 0); |
| int num_basic_block_pins = type->num_pins / type->capacity; |
| /* Logical location and physical location is offset by z * max_num_block_pins */ |
| |
| clb_pin = pb_pin + place_ctx.block_locs[clb].loc.z * num_basic_block_pins; |
| } else { |
| //No offset |
| clb_pin = pb_pin; |
| } |
| VTR_ASSERT(clb_pin >= 0); |
| |
| return clb_pin; |
| } |
| |
| bool is_clb_external_pin(ClusterBlockId blk_id, int pb_pin_id) { |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| |
| const t_pb_graph_pin* gpin = cluster_ctx.clb_nlist.block_pb(blk_id)->pb_route[pb_pin_id].pb_graph_pin; |
| VTR_ASSERT(gpin); |
| |
| //If the gpin's parent graph node is the same as the pb's graph node |
| //this must be a top level pin |
| const t_pb_graph_node* gnode = gpin->parent_node; |
| bool is_top_level_pin = (gnode == cluster_ctx.clb_nlist.block_pb(blk_id)->pb_graph_node); |
| |
| return is_top_level_pin; |
| } |
| |
| bool is_opin(int ipin, t_physical_tile_type_ptr type) { |
| /* Returns true if this clb pin is an output, false otherwise. */ |
| |
| if (ipin > type->num_pins) { |
| //Not a top level pin |
| return false; |
| } |
| |
| int iclass = type->pin_class[ipin]; |
| |
| if (type->class_inf[iclass].type == DRIVER) |
| return true; |
| else |
| return false; |
| } |
| |
| bool is_input_type(t_physical_tile_type_ptr type) { |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| return device_ctx.input_types.count(type); |
| } |
| |
| bool is_output_type(t_physical_tile_type_ptr type) { |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| return device_ctx.output_types.count(type); |
| } |
| |
| bool is_io_type(t_physical_tile_type_ptr type) { |
| return is_input_type(type) |
| || is_output_type(type); |
| } |
| |
| bool is_empty_type(t_physical_tile_type_ptr type) { |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| return type == device_ctx.EMPTY_TYPE; |
| } |
| |
| t_physical_tile_type_ptr physical_tile_type(t_logical_block_type_ptr logical_block_type) { |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| /* It is assumed that there is a 1:1 mapping between logical and physical types |
| * making it possible to use the same index to access the corresponding type |
| */ |
| return &device_ctx.physical_tile_types[logical_block_type->index]; |
| } |
| |
| t_physical_tile_type_ptr physical_tile_type(ClusterBlockId blk) { |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| |
| auto blk_type = cluster_ctx.clb_nlist.block_type(blk); |
| |
| return physical_tile_type(blk_type); |
| } |
| |
| t_logical_block_type_ptr logical_block_type(t_physical_tile_type_ptr physical_tile_type) { |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| /* It is assumed that there is a 1:1 mapping between logical and physical types |
| * making it possible to use the same index to access the corresponding type |
| */ |
| return &device_ctx.logical_block_types[physical_tile_type->index]; |
| } |
| |
| /* Each node in the pb_graph for a top-level pb_type can be uniquely identified |
| * by its pins. Since the pins in a cluster of a certain type are densely indexed, |
| * this function will find the pin index (int pin_count_in_cluster) of the first |
| * pin for a given pb_graph_node, and use this index value as unique identifier |
| * for the node. |
| */ |
| int get_unique_pb_graph_node_id(const t_pb_graph_node* pb_graph_node) { |
| t_pb_graph_pin first_input_pin; |
| t_pb_graph_pin first_output_pin; |
| int node_id; |
| |
| if (pb_graph_node->num_input_pins != nullptr) { |
| /* If input port exists on this node, return the index of the first |
| * input pin as node_id. |
| */ |
| first_input_pin = pb_graph_node->input_pins[0][0]; |
| node_id = first_input_pin.pin_count_in_cluster; |
| return node_id; |
| } else { |
| /* If no input port exists on node, then return the index of the first |
| * output pin. Every pb_node is guaranteed to have at least an input or |
| * output pin. |
| */ |
| first_output_pin = pb_graph_node->output_pins[0][0]; |
| node_id = first_output_pin.pin_count_in_cluster; |
| return node_id; |
| } |
| } |
| |
| void get_class_range_for_block(const ClusterBlockId blk_id, |
| int* class_low, |
| int* class_high) { |
| /* Assumes that the placement has been done so each block has a set of pins allocated to it */ |
| auto& place_ctx = g_vpr_ctx.placement(); |
| |
| auto type = physical_tile_type(blk_id); |
| VTR_ASSERT(type->num_class % type->capacity == 0); |
| *class_low = place_ctx.block_locs[blk_id].loc.z * (type->num_class / type->capacity); |
| *class_high = (place_ctx.block_locs[blk_id].loc.z + 1) * (type->num_class / type->capacity) - 1; |
| } |
| |
| void get_pin_range_for_block(const ClusterBlockId blk_id, |
| int* pin_low, |
| int* pin_high) { |
| /* Assumes that the placement has been done so each block has a set of pins allocated to it */ |
| auto& place_ctx = g_vpr_ctx.placement(); |
| |
| auto type = physical_tile_type(blk_id); |
| VTR_ASSERT(type->num_pins % type->capacity == 0); |
| *pin_low = place_ctx.block_locs[blk_id].loc.z * (type->num_pins / type->capacity); |
| *pin_high = (place_ctx.block_locs[blk_id].loc.z + 1) * (type->num_pins / type->capacity) - 1; |
| } |
| |
| t_physical_tile_type_ptr find_block_type_by_name(std::string name, const std::vector<t_physical_tile_type>& types) { |
| for (auto const& type : types) { |
| if (type.name == name) { |
| return &type; |
| } |
| } |
| return nullptr; //Not found |
| } |
| |
| t_logical_block_type_ptr infer_logic_block_type(const DeviceGrid& grid) { |
| auto& device_ctx = g_vpr_ctx.device(); |
| //Collect candidate blocks which contain LUTs |
| std::vector<t_logical_block_type_ptr> logic_block_candidates; |
| |
| for (const auto& type : device_ctx.logical_block_types) { |
| if (block_type_contains_blif_model(&type, LOGIC_MODEL_REGEX)) { |
| logic_block_candidates.push_back(&type); |
| } |
| } |
| |
| if (logic_block_candidates.empty()) { |
| //No LUTs, fallback to looking for which contain FFs |
| for (const auto& type : device_ctx.logical_block_types) { |
| if (block_type_contains_blif_model(&type, REGISTER_MODEL_REGEX)) { |
| logic_block_candidates.push_back(&type); |
| } |
| } |
| } |
| |
| //Sort the candidates by the most common block type |
| auto by_desc_grid_count = [&](t_logical_block_type_ptr lhs, t_logical_block_type_ptr rhs) { |
| return grid.num_instances(physical_tile_type(lhs)) > grid.num_instances(physical_tile_type(rhs)); |
| }; |
| std::stable_sort(logic_block_candidates.begin(), logic_block_candidates.end(), by_desc_grid_count); |
| |
| if (!logic_block_candidates.empty()) { |
| return logic_block_candidates.front(); |
| } else { |
| //Otherwise assume it is the most common block type |
| auto most_common_type = find_most_common_block_type(grid); |
| if (most_common_type == nullptr) { |
| VTR_LOG_WARN("Unable to infer which block type is a logic block\n"); |
| } |
| return most_common_type; |
| } |
| } |
| |
| t_logical_block_type_ptr find_most_common_block_type(const DeviceGrid& grid) { |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| t_physical_tile_type_ptr max_type = nullptr; |
| size_t max_count = 0; |
| for (const auto& type : device_ctx.physical_tile_types) { |
| size_t inst_cnt = grid.num_instances(&type); |
| if (max_count < inst_cnt) { |
| max_count = inst_cnt; |
| max_type = &type; |
| } |
| } |
| |
| if (max_type == nullptr) { |
| VTR_LOG_WARN("Unable to determine most common block type (perhaps the device grid was empty?)\n"); |
| return nullptr; |
| } |
| return logical_block_type(max_type); |
| } |
| |
| InstPort parse_inst_port(std::string str) { |
| InstPort inst_port(str); |
| |
| auto& device_ctx = g_vpr_ctx.device(); |
| auto blk_type = find_block_type_by_name(inst_port.instance_name(), device_ctx.physical_tile_types); |
| if (!blk_type) { |
| VPR_FATAL_ERROR(VPR_ERROR_ARCH, "Failed to find block type named %s", inst_port.instance_name().c_str()); |
| } |
| |
| const t_port* port = find_pb_graph_port(logical_block_type(blk_type)->pb_graph_head, inst_port.port_name()); |
| if (!port) { |
| VPR_FATAL_ERROR(VPR_ERROR_ARCH, "Failed to find port %s on block type %s", inst_port.port_name().c_str(), inst_port.instance_name().c_str()); |
| } |
| |
| if (inst_port.port_low_index() == InstPort::UNSPECIFIED) { |
| VTR_ASSERT(inst_port.port_high_index() == InstPort::UNSPECIFIED); |
| |
| inst_port.set_port_low_index(0); |
| inst_port.set_port_high_index(port->num_pins - 1); |
| |
| } else { |
| if (inst_port.port_low_index() < 0 || inst_port.port_low_index() >= port->num_pins |
| || inst_port.port_high_index() < 0 || inst_port.port_high_index() >= port->num_pins) { |
| VPR_FATAL_ERROR(VPR_ERROR_ARCH, "Pin indices [%d:%d] on port %s of block type %s out of expected range [%d:%d]", |
| inst_port.port_low_index(), inst_port.port_high_index(), |
| inst_port.port_name().c_str(), inst_port.instance_name().c_str(), |
| 0, port->num_pins - 1); |
| } |
| } |
| return inst_port; |
| } |
| |
| //Returns the pin class associated with the specified pin_index_in_port within the port port_name on type |
| int find_pin_class(t_logical_block_type_ptr type, std::string port_name, int pin_index_in_port, e_pin_type pin_type) { |
| int iclass = OPEN; |
| |
| int ipin = find_pin(type, port_name, pin_index_in_port); |
| |
| if (ipin != OPEN) { |
| iclass = physical_tile_type(type)->pin_class[ipin]; |
| |
| if (iclass != OPEN) { |
| VTR_ASSERT(physical_tile_type(type)->class_inf[iclass].type == pin_type); |
| } |
| } |
| return iclass; |
| } |
| |
| int find_pin(t_logical_block_type_ptr type, std::string port_name, int pin_index_in_port) { |
| int ipin = OPEN; |
| |
| t_pb_type* pb_type = type->pb_type; |
| t_port* matched_port = nullptr; |
| int port_base_ipin = 0; |
| for (int iport = 0; iport < pb_type->num_ports; ++iport) { |
| t_port* port = &pb_type->ports[iport]; |
| |
| if (port->name == port_name) { |
| matched_port = port; |
| break; |
| } |
| port_base_ipin += port->num_pins; |
| } |
| |
| if (matched_port) { |
| VTR_ASSERT(matched_port->name == port_name); |
| VTR_ASSERT(pin_index_in_port < matched_port->num_pins); |
| |
| ipin = port_base_ipin + pin_index_in_port; |
| } |
| |
| return ipin; |
| } |
| |
| //Returns true if the specified block type contains the specified blif model name |
| bool block_type_contains_blif_model(t_logical_block_type_ptr type, std::string blif_model_name) { |
| return pb_type_contains_blif_model(type->pb_type, blif_model_name); |
| } |
| |
| static bool block_type_contains_blif_model(t_logical_block_type_ptr type, const std::regex& blif_model_regex) { |
| return pb_type_contains_blif_model(type->pb_type, blif_model_regex); |
| } |
| |
| //Returns true of a pb_type (or it's children) contain the specified blif model name |
| bool pb_type_contains_blif_model(const t_pb_type* pb_type, const std::string& blif_model_name) { |
| if (!pb_type) { |
| return false; |
| } |
| |
| if (pb_type->blif_model != nullptr) { |
| //Leaf pb_type |
| VTR_ASSERT(pb_type->num_modes == 0); |
| if (blif_model_name == pb_type->blif_model |
| || ".subckt " + blif_model_name == pb_type->blif_model) { |
| return true; |
| } else { |
| return false; |
| } |
| } else { |
| for (int imode = 0; imode < pb_type->num_modes; ++imode) { |
| const t_mode* mode = &pb_type->modes[imode]; |
| |
| for (int ichild = 0; ichild < mode->num_pb_type_children; ++ichild) { |
| const t_pb_type* pb_type_child = &mode->pb_type_children[ichild]; |
| if (pb_type_contains_blif_model(pb_type_child, blif_model_name)) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| static bool pb_type_contains_blif_model(const t_pb_type* pb_type, const std::regex& blif_model_regex) { |
| if (!pb_type) { |
| return false; |
| } |
| |
| if (pb_type->blif_model != nullptr) { |
| //Leaf pb_type |
| VTR_ASSERT(pb_type->num_modes == 0); |
| if (std::regex_match(pb_type->blif_model, blif_model_regex)) { |
| return true; |
| } else { |
| return false; |
| } |
| } else { |
| for (int imode = 0; imode < pb_type->num_modes; ++imode) { |
| const t_mode* mode = &pb_type->modes[imode]; |
| |
| for (int ichild = 0; ichild < mode->num_pb_type_children; ++ichild) { |
| const t_pb_type* pb_type_child = &mode->pb_type_children[ichild]; |
| if (pb_type_contains_blif_model(pb_type_child, blif_model_regex)) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| int get_max_primitives_in_pb_type(t_pb_type* pb_type) { |
| int i, j; |
| int max_size, temp_size; |
| if (pb_type->modes == nullptr) { |
| max_size = 1; |
| } else { |
| max_size = 0; |
| temp_size = 0; |
| for (i = 0; i < pb_type->num_modes; i++) { |
| for (j = 0; j < pb_type->modes[i].num_pb_type_children; j++) { |
| temp_size += pb_type->modes[i].pb_type_children[j].num_pb |
| * get_max_primitives_in_pb_type( |
| &pb_type->modes[i].pb_type_children[j]); |
| } |
| if (temp_size > max_size) { |
| max_size = temp_size; |
| } |
| } |
| } |
| return max_size; |
| } |
| |
| /* finds maximum number of nets that can be contained in pb_type, this is bounded by the number of driving pins */ |
| int get_max_nets_in_pb_type(const t_pb_type* pb_type) { |
| int i, j; |
| int max_nets, temp_nets; |
| if (pb_type->modes == nullptr) { |
| max_nets = pb_type->num_output_pins; |
| } else { |
| max_nets = 0; |
| for (i = 0; i < pb_type->num_modes; i++) { |
| temp_nets = 0; |
| for (j = 0; j < pb_type->modes[i].num_pb_type_children; j++) { |
| temp_nets += pb_type->modes[i].pb_type_children[j].num_pb |
| * get_max_nets_in_pb_type( |
| &pb_type->modes[i].pb_type_children[j]); |
| } |
| if (temp_nets > max_nets) { |
| max_nets = temp_nets; |
| } |
| } |
| } |
| if (pb_type->parent_mode == nullptr) { |
| max_nets += pb_type->num_input_pins + pb_type->num_output_pins |
| + pb_type->num_clock_pins; |
| } |
| return max_nets; |
| } |
| |
| int get_max_depth_of_pb_type(t_pb_type* pb_type) { |
| int i, j; |
| int max_depth, temp_depth; |
| max_depth = pb_type->depth; |
| for (i = 0; i < pb_type->num_modes; i++) { |
| for (j = 0; j < pb_type->modes[i].num_pb_type_children; j++) { |
| temp_depth = get_max_depth_of_pb_type( |
| &pb_type->modes[i].pb_type_children[j]); |
| if (temp_depth > max_depth) { |
| max_depth = temp_depth; |
| } |
| } |
| } |
| return max_depth; |
| } |
| |
| /** |
| * given an atom block and physical primitive type, is the mapping legal |
| */ |
| bool primitive_type_feasible(const AtomBlockId blk_id, const t_pb_type* cur_pb_type) { |
| if (cur_pb_type == nullptr) { |
| return false; |
| } |
| |
| auto& atom_ctx = g_vpr_ctx.atom(); |
| if (cur_pb_type->model != atom_ctx.nlist.block_model(blk_id)) { |
| //Primitive and atom do not match |
| return false; |
| } |
| |
| VTR_ASSERT_MSG(atom_ctx.nlist.is_compressed(), "This function assumes a compressed/non-dirty netlist"); |
| |
| //Keep track of how many atom ports were checked. |
| // |
| //We need to do this since we iterate over the pb's ports and |
| //may miss some atom ports if there is a mismatch |
| size_t checked_ports = 0; |
| |
| //Look at each port on the pb and find the associated port on the |
| //atom. To be feasible the pb must have as many pins on each port |
| //as the atom requires |
| for (int iport = 0; iport < cur_pb_type->num_ports; ++iport) { |
| const t_port* pb_port = &cur_pb_type->ports[iport]; |
| const t_model_ports* pb_model_port = pb_port->model_port; |
| |
| //Find the matching port on the atom |
| auto port_id = atom_ctx.nlist.find_atom_port(blk_id, pb_model_port); |
| |
| if (port_id) { //Port is used by the atom |
| |
| //In compressed form the atom netlist stores only in-use pins, |
| //so we can query the number of required pins directly |
| int required_atom_pins = atom_ctx.nlist.port_pins(port_id).size(); |
| |
| int available_pb_pins = pb_port->num_pins; |
| |
| if (available_pb_pins < required_atom_pins) { |
| //Too many pins required |
| return false; |
| } |
| |
| //Note that this port was checked |
| ++checked_ports; |
| } |
| } |
| |
| //Similarly to pins, only in-use ports are stored in the compressed |
| //atom netlist, so we can figure out how many ports should have been |
| //checked directly |
| size_t atom_ports = atom_ctx.nlist.block_ports(blk_id).size(); |
| |
| //See if all the atom ports were checked |
| if (checked_ports != atom_ports) { |
| VTR_ASSERT(checked_ports < atom_ports); |
| //Required atom port was missing from physical primitive |
| return false; |
| } |
| |
| //Feasible |
| return true; |
| } |
| |
| //Returns the sibling atom of a memory slice pb |
| // Note that the pb must be part of a MEMORY_CLASS |
| AtomBlockId find_memory_sibling(const t_pb* pb) { |
| auto& atom_ctx = g_vpr_ctx.atom(); |
| |
| const t_pb_type* pb_type = pb->pb_graph_node->pb_type; |
| |
| VTR_ASSERT(pb_type->class_type == MEMORY_CLASS); |
| |
| const t_pb* memory_class_pb = pb->parent_pb; |
| |
| for (int isibling = 0; isibling < pb_type->parent_mode->num_pb_type_children; ++isibling) { |
| const t_pb* sibling_pb = &memory_class_pb->child_pbs[pb->mode][isibling]; |
| |
| if (sibling_pb->name != nullptr) { |
| return atom_ctx.lookup.pb_atom(sibling_pb); |
| } |
| } |
| return AtomBlockId::INVALID(); |
| } |
| |
| /** |
| * Return pb_graph_node pin from model port and pin |
| * NULL if not found |
| */ |
| t_pb_graph_pin* get_pb_graph_node_pin_from_model_port_pin(const t_model_ports* model_port, const int model_pin, const t_pb_graph_node* pb_graph_node) { |
| int i; |
| |
| if (model_port->dir == IN_PORT) { |
| if (model_port->is_clock == false) { |
| for (i = 0; i < pb_graph_node->num_input_ports; i++) { |
| if (pb_graph_node->input_pins[i][0].port->model_port == model_port) { |
| if (pb_graph_node->num_input_pins[i] > model_pin) { |
| return &pb_graph_node->input_pins[i][model_pin]; |
| } else { |
| return nullptr; |
| } |
| } |
| } |
| } else { |
| for (i = 0; i < pb_graph_node->num_clock_ports; i++) { |
| if (pb_graph_node->clock_pins[i][0].port->model_port == model_port) { |
| if (pb_graph_node->num_clock_pins[i] > model_pin) { |
| return &pb_graph_node->clock_pins[i][model_pin]; |
| } else { |
| return nullptr; |
| } |
| } |
| } |
| } |
| } else { |
| VTR_ASSERT(model_port->dir == OUT_PORT); |
| for (i = 0; i < pb_graph_node->num_output_ports; i++) { |
| if (pb_graph_node->output_pins[i][0].port->model_port == model_port) { |
| if (pb_graph_node->num_output_pins[i] > model_pin) { |
| return &pb_graph_node->output_pins[i][model_pin]; |
| } else { |
| return nullptr; |
| } |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| //Retrieves the atom pin associated with a specific CLB and pb_graph_pin |
| AtomPinId find_atom_pin(ClusterBlockId blk_id, const t_pb_graph_pin* pb_gpin) { |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| auto& atom_ctx = g_vpr_ctx.atom(); |
| |
| int pb_route_id = pb_gpin->pin_count_in_cluster; |
| AtomNetId atom_net = cluster_ctx.clb_nlist.block_pb(blk_id)->pb_route[pb_route_id].atom_net_id; |
| |
| VTR_ASSERT(atom_net); |
| |
| AtomPinId atom_pin; |
| |
| //Look through all the pins on this net, looking for the matching pin |
| for (AtomPinId pin : atom_ctx.nlist.net_pins(atom_net)) { |
| AtomBlockId blk = atom_ctx.nlist.pin_block(pin); |
| if (atom_ctx.lookup.atom_clb(blk) == blk_id) { |
| //Part of the same CLB |
| if (atom_ctx.lookup.atom_pin_pb_graph_pin(pin) == pb_gpin) |
| //The same pin |
| atom_pin = pin; |
| } |
| } |
| |
| VTR_ASSERT(atom_pin); |
| |
| return atom_pin; |
| } |
| |
| //Retrieves the pb_graph_pin associated with an AtomPinId |
| // Currently this function just wraps get_pb_graph_node_pin_from_model_port_pin() |
| // in a more convenient interface. |
| const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomLookup& netlist_lookup, const AtomPinId pin_id) { |
| VTR_ASSERT(pin_id); |
| |
| //Get the graph node |
| AtomBlockId blk_id = netlist.pin_block(pin_id); |
| const t_pb_graph_node* pb_gnode = netlist_lookup.atom_pb_graph_node(blk_id); |
| VTR_ASSERT(pb_gnode); |
| |
| //The graph node and pin/block should agree on the model they represent |
| VTR_ASSERT(netlist.block_model(blk_id) == pb_gnode->pb_type->model); |
| |
| //Get the pin index |
| AtomPortId port_id = netlist.pin_port(pin_id); |
| int ipin = netlist.pin_port_bit(pin_id); |
| |
| //Get the model port |
| const t_model_ports* model_port = netlist.port_model(port_id); |
| VTR_ASSERT(model_port); |
| |
| return get_pb_graph_node_pin_from_model_port_pin(model_port, ipin, pb_gnode); |
| } |
| |
| t_pb_graph_pin* get_pb_graph_node_pin_from_block_pin(ClusterBlockId iblock, int ipin) { |
| int i, count; |
| const t_pb_type* pb_type; |
| t_pb_graph_node* pb_graph_node; |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| |
| pb_graph_node = cluster_ctx.clb_nlist.block_pb(iblock)->pb_graph_node; |
| pb_type = pb_graph_node->pb_type; |
| |
| /* If this is post-placed, then the ipin may have been shuffled up by the z * num_pins, |
| * bring it back down to 0..num_pins-1 range for easier analysis */ |
| ipin %= (pb_type->num_input_pins + pb_type->num_output_pins + pb_type->num_clock_pins); |
| |
| if (ipin < pb_type->num_input_pins) { |
| count = ipin; |
| for (i = 0; i < pb_graph_node->num_input_ports; i++) { |
| if (count - pb_graph_node->num_input_pins[i] >= 0) { |
| count -= pb_graph_node->num_input_pins[i]; |
| } else { |
| return &pb_graph_node->input_pins[i][count]; |
| } |
| } |
| } else if (ipin < pb_type->num_input_pins + pb_type->num_output_pins) { |
| count = ipin - pb_type->num_input_pins; |
| for (i = 0; i < pb_graph_node->num_output_ports; i++) { |
| if (count - pb_graph_node->num_output_pins[i] >= 0) { |
| count -= pb_graph_node->num_output_pins[i]; |
| } else { |
| return &pb_graph_node->output_pins[i][count]; |
| } |
| } |
| } else { |
| count = ipin - pb_type->num_input_pins - pb_type->num_output_pins; |
| for (i = 0; i < pb_graph_node->num_clock_ports; i++) { |
| if (count - pb_graph_node->num_clock_pins[i] >= 0) { |
| count -= pb_graph_node->num_clock_pins[i]; |
| } else { |
| return &pb_graph_node->clock_pins[i][count]; |
| } |
| } |
| } |
| VTR_ASSERT(0); |
| return nullptr; |
| } |
| |
| const t_port* find_pb_graph_port(const t_pb_graph_node* pb_gnode, std::string port_name) { |
| const t_pb_graph_pin* gpin = find_pb_graph_pin(pb_gnode, port_name, 0); |
| |
| if (gpin != nullptr) { |
| return gpin->port; |
| } |
| return nullptr; |
| } |
| |
| const t_pb_graph_pin* find_pb_graph_pin(const t_pb_graph_node* pb_gnode, std::string port_name, int index) { |
| for (int iport = 0; iport < pb_gnode->num_input_ports; iport++) { |
| if (pb_gnode->num_input_pins[iport] < index) continue; |
| |
| const t_pb_graph_pin* gpin = &pb_gnode->input_pins[iport][index]; |
| |
| if (gpin->port->name == port_name) { |
| return gpin; |
| } |
| } |
| for (int iport = 0; iport < pb_gnode->num_output_ports; iport++) { |
| if (pb_gnode->num_output_pins[iport] < index) continue; |
| |
| const t_pb_graph_pin* gpin = &pb_gnode->output_pins[iport][index]; |
| |
| if (gpin->port->name == port_name) { |
| return gpin; |
| } |
| } |
| for (int iport = 0; iport < pb_gnode->num_clock_ports; iport++) { |
| if (pb_gnode->num_clock_pins[iport] < index) continue; |
| |
| const t_pb_graph_pin* gpin = &pb_gnode->clock_pins[iport][index]; |
| |
| if (gpin->port->name == port_name) { |
| return gpin; |
| } |
| } |
| |
| //Not found |
| return nullptr; |
| } |
| |
| /* Recusively visit through all pb_graph_nodes to populate pb_graph_pin_lookup_from_index */ |
| static void load_pb_graph_pin_lookup_from_index_rec(t_pb_graph_pin** pb_graph_pin_lookup_from_index, t_pb_graph_node* pb_graph_node) { |
| for (int iport = 0; iport < pb_graph_node->num_input_ports; iport++) { |
| for (int ipin = 0; ipin < pb_graph_node->num_input_pins[iport]; ipin++) { |
| t_pb_graph_pin* pb_pin = &pb_graph_node->input_pins[iport][ipin]; |
| VTR_ASSERT(pb_graph_pin_lookup_from_index[pb_pin->pin_count_in_cluster] == nullptr); |
| pb_graph_pin_lookup_from_index[pb_pin->pin_count_in_cluster] = pb_pin; |
| } |
| } |
| for (int iport = 0; iport < pb_graph_node->num_output_ports; iport++) { |
| for (int ipin = 0; ipin < pb_graph_node->num_output_pins[iport]; ipin++) { |
| t_pb_graph_pin* pb_pin = &pb_graph_node->output_pins[iport][ipin]; |
| VTR_ASSERT(pb_graph_pin_lookup_from_index[pb_pin->pin_count_in_cluster] == nullptr); |
| pb_graph_pin_lookup_from_index[pb_pin->pin_count_in_cluster] = pb_pin; |
| } |
| } |
| for (int iport = 0; iport < pb_graph_node->num_clock_ports; iport++) { |
| for (int ipin = 0; ipin < pb_graph_node->num_clock_pins[iport]; ipin++) { |
| t_pb_graph_pin* pb_pin = &pb_graph_node->clock_pins[iport][ipin]; |
| VTR_ASSERT(pb_graph_pin_lookup_from_index[pb_pin->pin_count_in_cluster] == nullptr); |
| pb_graph_pin_lookup_from_index[pb_pin->pin_count_in_cluster] = pb_pin; |
| } |
| } |
| |
| for (int imode = 0; imode < pb_graph_node->pb_type->num_modes; imode++) { |
| for (int ipb_type = 0; ipb_type < pb_graph_node->pb_type->modes[imode].num_pb_type_children; ipb_type++) { |
| for (int ipb = 0; ipb < pb_graph_node->pb_type->modes[imode].pb_type_children[ipb_type].num_pb; ipb++) { |
| load_pb_graph_pin_lookup_from_index_rec(pb_graph_pin_lookup_from_index, &pb_graph_node->child_pb_graph_nodes[imode][ipb_type][ipb]); |
| } |
| } |
| } |
| } |
| |
| /* Create a lookup that returns a pb_graph_pin pointer given the pb_graph_pin index */ |
| t_pb_graph_pin** alloc_and_load_pb_graph_pin_lookup_from_index(t_logical_block_type_ptr type) { |
| t_pb_graph_pin** pb_graph_pin_lookup_from_type = nullptr; |
| |
| t_pb_graph_node* pb_graph_head = type->pb_graph_head; |
| if (pb_graph_head == nullptr) { |
| return nullptr; |
| } |
| int num_pins = pb_graph_head->total_pb_pins; |
| |
| VTR_ASSERT(num_pins > 0); |
| |
| pb_graph_pin_lookup_from_type = new t_pb_graph_pin*[num_pins]; |
| for (int id = 0; id < num_pins; id++) { |
| pb_graph_pin_lookup_from_type[id] = nullptr; |
| } |
| |
| VTR_ASSERT(pb_graph_pin_lookup_from_type); |
| |
| load_pb_graph_pin_lookup_from_index_rec(pb_graph_pin_lookup_from_type, pb_graph_head); |
| |
| for (int id = 0; id < num_pins; id++) { |
| VTR_ASSERT(pb_graph_pin_lookup_from_type[id] != nullptr); |
| } |
| |
| return pb_graph_pin_lookup_from_type; |
| } |
| |
| /* Free pb_graph_pin lookup array */ |
| void free_pb_graph_pin_lookup_from_index(t_pb_graph_pin** pb_graph_pin_lookup_from_type) { |
| if (pb_graph_pin_lookup_from_type == nullptr) { |
| return; |
| } |
| delete[] pb_graph_pin_lookup_from_type; |
| } |
| |
| /** |
| * Create lookup table that returns a pointer to the pb given [index to block][pin_id]. |
| */ |
| vtr::vector<ClusterBlockId, t_pb**> alloc_and_load_pin_id_to_pb_mapping() { |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| |
| vtr::vector<ClusterBlockId, t_pb**> pin_id_to_pb_mapping(cluster_ctx.clb_nlist.blocks().size()); |
| for (auto blk_id : cluster_ctx.clb_nlist.blocks()) { |
| pin_id_to_pb_mapping[blk_id] = new t_pb*[cluster_ctx.clb_nlist.block_type(blk_id)->pb_graph_head->total_pb_pins]; |
| for (int j = 0; j < cluster_ctx.clb_nlist.block_type(blk_id)->pb_graph_head->total_pb_pins; j++) { |
| pin_id_to_pb_mapping[blk_id][j] = nullptr; |
| } |
| load_pin_id_to_pb_mapping_rec(cluster_ctx.clb_nlist.block_pb(blk_id), pin_id_to_pb_mapping[blk_id]); |
| } |
| return pin_id_to_pb_mapping; |
| } |
| |
| /** |
| * Recursively create lookup table that returns a pointer to the pb given a pb_graph_pin id. |
| */ |
| static void load_pin_id_to_pb_mapping_rec(t_pb* cur_pb, t_pb** pin_id_to_pb_mapping) { |
| t_pb_graph_node* pb_graph_node = cur_pb->pb_graph_node; |
| t_pb_type* pb_type = pb_graph_node->pb_type; |
| int mode = cur_pb->mode; |
| |
| for (int i = 0; i < pb_graph_node->num_input_ports; i++) { |
| for (int j = 0; j < pb_graph_node->num_input_pins[i]; j++) { |
| pin_id_to_pb_mapping[pb_graph_node->input_pins[i][j].pin_count_in_cluster] = cur_pb; |
| } |
| } |
| for (int i = 0; i < pb_graph_node->num_output_ports; i++) { |
| for (int j = 0; j < pb_graph_node->num_output_pins[i]; j++) { |
| pin_id_to_pb_mapping[pb_graph_node->output_pins[i][j].pin_count_in_cluster] = cur_pb; |
| } |
| } |
| for (int i = 0; i < pb_graph_node->num_clock_ports; i++) { |
| for (int j = 0; j < pb_graph_node->num_clock_pins[i]; j++) { |
| pin_id_to_pb_mapping[pb_graph_node->clock_pins[i][j].pin_count_in_cluster] = cur_pb; |
| } |
| } |
| |
| if (pb_type->num_modes == 0 || cur_pb->child_pbs == nullptr) { |
| return; |
| } |
| |
| for (int i = 0; i < pb_type->modes[mode].num_pb_type_children; i++) { |
| for (int j = 0; j < pb_type->modes[mode].pb_type_children[i].num_pb; j++) { |
| load_pin_id_to_pb_mapping_rec(&cur_pb->child_pbs[i][j], pin_id_to_pb_mapping); |
| } |
| } |
| } |
| |
| /** |
| * free pin_index_to_pb_mapping lookup table |
| */ |
| void free_pin_id_to_pb_mapping(vtr::vector<ClusterBlockId, t_pb**>& pin_id_to_pb_mapping) { |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| for (auto blk_id : cluster_ctx.clb_nlist.blocks()) { |
| delete[] pin_id_to_pb_mapping[blk_id]; |
| } |
| pin_id_to_pb_mapping.clear(); |
| } |
| |
| /** |
| * Determine cost for using primitive within a complex block, should use primitives of low cost before selecting primitives of high cost |
| * For now, assume primitives that have a lot of pins are scarcer than those without so use primitives with less pins before those with more |
| */ |
| float compute_primitive_base_cost(const t_pb_graph_node* primitive) { |
| return (primitive->pb_type->num_input_pins |
| + primitive->pb_type->num_output_pins |
| + primitive->pb_type->num_clock_pins); |
| } |
| |
| int num_ext_inputs_atom_block(AtomBlockId blk_id) { |
| /* Returns the number of input pins on this atom block that must be hooked * |
| * up through external interconnect. That is, the number of input * |
| * pins used - the number which connect (internally) to the outputs. */ |
| |
| int ext_inps = 0; |
| |
| /* TODO: process to get ext_inps is slow, should cache in lookup table */ |
| |
| std::unordered_set<AtomNetId> input_nets; |
| |
| //Record the unique input nets |
| auto& atom_ctx = g_vpr_ctx.atom(); |
| for (auto pin_id : atom_ctx.nlist.block_input_pins(blk_id)) { |
| auto net_id = atom_ctx.nlist.pin_net(pin_id); |
| input_nets.insert(net_id); |
| } |
| |
| ext_inps = input_nets.size(); |
| |
| //Look through the output nets for any duplicates of the input nets |
| for (auto pin_id : atom_ctx.nlist.block_output_pins(blk_id)) { |
| auto net_id = atom_ctx.nlist.pin_net(pin_id); |
| if (input_nets.count(net_id)) { |
| --ext_inps; |
| } |
| } |
| |
| VTR_ASSERT(ext_inps >= 0); |
| |
| return (ext_inps); |
| } |
| |
| void free_pb(t_pb* pb) { |
| if (pb == nullptr) { |
| return; |
| } |
| |
| const t_pb_type* pb_type; |
| int i, j, mode; |
| |
| pb_type = pb->pb_graph_node->pb_type; |
| |
| if (pb->name) { |
| free(pb->name); |
| pb->name = nullptr; |
| } |
| |
| if (pb_type->blif_model == nullptr) { |
| mode = pb->mode; |
| for (i = 0; i < pb_type->modes[mode].num_pb_type_children && pb->child_pbs != nullptr; i++) { |
| for (j = 0; j < pb_type->modes[mode].pb_type_children[i].num_pb && pb->child_pbs[i] != nullptr; j++) { |
| if (pb->child_pbs[i][j].name != nullptr || pb->child_pbs[i][j].child_pbs != nullptr) { |
| free_pb(&pb->child_pbs[i][j]); |
| } |
| } |
| if (pb->child_pbs[i]) { |
| //Free children (num_pb) |
| delete[] pb->child_pbs[i]; |
| } |
| } |
| if (pb->child_pbs) { |
| //Free child pointers (modes) |
| delete[] pb->child_pbs; |
| } |
| |
| pb->child_pbs = nullptr; |
| |
| } else { |
| /* Primitive */ |
| auto& atom_ctx = g_vpr_ctx.mutable_atom(); |
| auto blk_id = atom_ctx.lookup.pb_atom(pb); |
| if (blk_id) { |
| //Update atom netlist mapping |
| atom_ctx.lookup.set_atom_clb(blk_id, ClusterBlockId::INVALID()); |
| atom_ctx.lookup.set_atom_pb(blk_id, nullptr); |
| } |
| atom_ctx.lookup.set_atom_pb(AtomBlockId::INVALID(), pb); |
| } |
| free_pb_stats(pb); |
| } |
| |
| void revalid_molecules(const t_pb* pb, const std::multimap<AtomBlockId, t_pack_molecule*>& atom_molecules) { |
| const t_pb_type* pb_type = pb->pb_graph_node->pb_type; |
| |
| if (pb_type->blif_model == nullptr) { |
| int mode = pb->mode; |
| for (int i = 0; i < pb_type->modes[mode].num_pb_type_children && pb->child_pbs != nullptr; i++) { |
| for (int j = 0; j < pb_type->modes[mode].pb_type_children[i].num_pb && pb->child_pbs[i] != nullptr; j++) { |
| if (pb->child_pbs[i][j].name != nullptr || pb->child_pbs[i][j].child_pbs != nullptr) { |
| revalid_molecules(&pb->child_pbs[i][j], atom_molecules); |
| } |
| } |
| } |
| } else { |
| //Primitive |
| auto& atom_ctx = g_vpr_ctx.mutable_atom(); |
| |
| auto blk_id = atom_ctx.lookup.pb_atom(pb); |
| if (blk_id) { |
| /* If any molecules were marked invalid because of this logic block getting packed, mark them valid */ |
| |
| //Update atom netlist mapping |
| atom_ctx.lookup.set_atom_clb(blk_id, ClusterBlockId::INVALID()); |
| atom_ctx.lookup.set_atom_pb(blk_id, nullptr); |
| |
| auto rng = atom_molecules.equal_range(blk_id); |
| for (const auto& kv : vtr::make_range(rng.first, rng.second)) { |
| t_pack_molecule* cur_molecule = kv.second; |
| if (cur_molecule->valid == false) { |
| int i; |
| for (i = 0; i < get_array_size_of_molecule(cur_molecule); i++) { |
| if (cur_molecule->atom_block_ids[i]) { |
| if (atom_ctx.lookup.atom_clb(cur_molecule->atom_block_ids[i]) != ClusterBlockId::INVALID()) { |
| break; |
| } |
| } |
| } |
| /* All atom blocks are open for this molecule, place back in queue */ |
| if (i == get_array_size_of_molecule(cur_molecule)) { |
| cur_molecule->valid = true; |
| // when invalidating a molecule check if it's a chain molecule |
| // that is part of a long chain. If so, check if this molecule |
| // have modified the chain_id value based on the stale packing |
| // then reset the chain id and the first packed molecule pointer |
| // this is packing is being reset |
| if (cur_molecule->is_chain() && cur_molecule->chain_info->is_long_chain && cur_molecule->chain_info->first_packed_molecule == cur_molecule) { |
| cur_molecule->chain_info->first_packed_molecule = nullptr; |
| cur_molecule->chain_info->chain_id = -1; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void free_pb_stats(t_pb* pb) { |
| if (pb) { |
| if (pb->pb_stats == nullptr) { |
| return; |
| } |
| |
| pb->pb_stats->gain.clear(); |
| pb->pb_stats->timinggain.clear(); |
| pb->pb_stats->sharinggain.clear(); |
| pb->pb_stats->hillgain.clear(); |
| pb->pb_stats->connectiongain.clear(); |
| pb->pb_stats->num_pins_of_net_in_pb.clear(); |
| |
| if (pb->pb_stats->feasible_blocks) { |
| free(pb->pb_stats->feasible_blocks); |
| } |
| if (!pb->parent_pb) { |
| pb->pb_stats->transitive_fanout_candidates.clear(); |
| } |
| delete pb->pb_stats; |
| pb->pb_stats = nullptr; |
| } |
| } |
| |
| /*************************************************************************************** |
| * Y.G.THIEN |
| * 29 AUG 2012 |
| * |
| * The following functions maps the block pins indices for all block types to the * |
| * corresponding port indices and port_pin indices. This is necessary since there are * |
| * different netlist conventions - in the cluster level, ports and port pins are used * |
| * while in the post-pack level, block pins are used. * |
| * * |
| ***************************************************************************************/ |
| |
| void get_port_pin_from_blk_pin(int blk_type_index, int blk_pin, int* port, int* port_pin) { |
| /* These two mappings are needed since there are two different netlist * |
| * conventions - in the cluster level, ports and port pins are used * |
| * while in the post-pack level, block pins are used. The reason block * |
| * type is used instead of blocks is that the mapping is the same for * |
| * blocks belonging to the same block type. * |
| * * |
| * f_port_from_blk_pin array allow us to quickly find what port a * |
| * block pin corresponds to. * |
| * [0...device_ctx.logical_block_types.size()-1][0...blk_pin_count-1] * |
| * * |
| * f_port_pin_from_blk_pin array allow us to quickly find what port * |
| * pin a block pin corresponds to. * |
| * [0...device_ctx.logical_block_types.size()-1][0...blk_pin_count-1] */ |
| |
| /* If either one of the arrays is not allocated and loaded, it is * |
| * corrupted, so free both of them. */ |
| if ((f_port_from_blk_pin == nullptr && f_port_pin_from_blk_pin != nullptr) |
| || (f_port_from_blk_pin != nullptr && f_port_pin_from_blk_pin == nullptr)) { |
| free_port_pin_from_blk_pin(); |
| } |
| |
| /* If the arrays are not allocated and loaded, allocate it. */ |
| if (f_port_from_blk_pin == nullptr && f_port_pin_from_blk_pin == nullptr) { |
| alloc_and_load_port_pin_from_blk_pin(); |
| } |
| |
| /* Return the port and port_pin for the pin. */ |
| *port = f_port_from_blk_pin[blk_type_index][blk_pin]; |
| *port_pin = f_port_pin_from_blk_pin[blk_type_index][blk_pin]; |
| } |
| |
| void free_port_pin_from_blk_pin() { |
| /* Frees the f_port_from_blk_pin and f_port_pin_from_blk_pin arrays. * |
| * * |
| * This function is called when the file-scope arrays are corrupted. * |
| * Otherwise, the arrays are freed in free_placement_structs() */ |
| |
| unsigned int itype; |
| |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| if (f_port_from_blk_pin != nullptr) { |
| for (itype = 1; itype < device_ctx.logical_block_types.size(); itype++) { |
| free(f_port_from_blk_pin[itype]); |
| } |
| free(f_port_from_blk_pin); |
| |
| f_port_from_blk_pin = nullptr; |
| } |
| |
| if (f_port_pin_from_blk_pin != nullptr) { |
| for (itype = 1; itype < device_ctx.logical_block_types.size(); itype++) { |
| free(f_port_pin_from_blk_pin[itype]); |
| } |
| free(f_port_pin_from_blk_pin); |
| |
| f_port_pin_from_blk_pin = nullptr; |
| } |
| } |
| |
| static void alloc_and_load_port_pin_from_blk_pin() { |
| /* Allocates and loads f_port_from_blk_pin and f_port_pin_from_blk_pin * |
| * arrays. * |
| * * |
| * The arrays are freed in free_placement_structs() */ |
| |
| int** temp_port_from_blk_pin = nullptr; |
| int** temp_port_pin_from_blk_pin = nullptr; |
| unsigned int itype; |
| int iblk_pin, iport, iport_pin; |
| int blk_pin_count, num_port_pins, num_ports; |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| /* Allocate and initialize the values to OPEN (-1). */ |
| temp_port_from_blk_pin = (int**)vtr::malloc(device_ctx.logical_block_types.size() * sizeof(int*)); |
| temp_port_pin_from_blk_pin = (int**)vtr::malloc(device_ctx.logical_block_types.size() * sizeof(int*)); |
| for (const auto& type : device_ctx.logical_block_types) { |
| itype = type.index; |
| blk_pin_count = physical_tile_type(&type)->num_pins; |
| |
| temp_port_from_blk_pin[itype] = (int*)vtr::malloc(blk_pin_count * sizeof(int)); |
| temp_port_pin_from_blk_pin[itype] = (int*)vtr::malloc(blk_pin_count * sizeof(int)); |
| |
| for (iblk_pin = 0; iblk_pin < blk_pin_count; iblk_pin++) { |
| temp_port_from_blk_pin[itype][iblk_pin] = OPEN; |
| temp_port_pin_from_blk_pin[itype][iblk_pin] = OPEN; |
| } |
| } |
| |
| /* Load the values */ |
| for (const auto& type : device_ctx.logical_block_types) { |
| itype = type.index; |
| |
| /* itype starts from 1 since device_ctx.logical_block_types[0] is the EMPTY_TYPE. */ |
| if (itype == 0) { |
| continue; |
| } |
| |
| blk_pin_count = 0; |
| num_ports = type.pb_type->num_ports; |
| |
| for (iport = 0; iport < num_ports; iport++) { |
| num_port_pins = type.pb_type->ports[iport].num_pins; |
| |
| for (iport_pin = 0; iport_pin < num_port_pins; iport_pin++) { |
| temp_port_from_blk_pin[itype][blk_pin_count] = iport; |
| temp_port_pin_from_blk_pin[itype][blk_pin_count] = iport_pin; |
| blk_pin_count++; |
| } |
| } |
| } |
| |
| /* Sets the file_scope variables to point at the arrays. */ |
| f_port_from_blk_pin = temp_port_from_blk_pin; |
| f_port_pin_from_blk_pin = temp_port_pin_from_blk_pin; |
| } |
| |
| void get_blk_pin_from_port_pin(int blk_type_index, int port, int port_pin, int* blk_pin) { |
| /* This mapping is needed since there are two different netlist * |
| * conventions - in the cluster level, ports and port pins are used * |
| * while in the post-pack level, block pins are used. The reason block * |
| * type is used instead of blocks is to save memories. * |
| * * |
| * f_port_pin_to_block_pin array allows us to quickly find what block * |
| * pin a port pin corresponds to. * |
| * [0...device_ctx.logical_block_types.size()-1][0...num_ports-1][0...num_port_pins-1] */ |
| |
| /* If the array is not allocated and loaded, allocate it. */ |
| if (f_blk_pin_from_port_pin == nullptr) { |
| alloc_and_load_blk_pin_from_port_pin(); |
| } |
| |
| /* Return the port and port_pin for the pin. */ |
| *blk_pin = f_blk_pin_from_port_pin[blk_type_index][port][port_pin]; |
| } |
| |
| void free_blk_pin_from_port_pin() { |
| /* Frees the f_blk_pin_from_port_pin array. * |
| * * |
| * This function is called when the arrays are freed in * |
| * free_placement_structs() */ |
| |
| int iport, num_ports; |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| if (f_blk_pin_from_port_pin != nullptr) { |
| for (const auto& type : device_ctx.logical_block_types) { |
| int itype = type.index; |
| |
| // Avoid EMPTY_TYPE |
| if (itype == 0) { |
| continue; |
| } |
| |
| num_ports = type.pb_type->num_ports; |
| for (iport = 0; iport < num_ports; iport++) { |
| free(f_blk_pin_from_port_pin[itype][iport]); |
| } |
| free(f_blk_pin_from_port_pin[itype]); |
| } |
| free(f_blk_pin_from_port_pin); |
| |
| f_blk_pin_from_port_pin = nullptr; |
| } |
| } |
| |
| static void alloc_and_load_blk_pin_from_port_pin() { |
| /* Allocates and loads blk_pin_from_port_pin array. * |
| * * |
| * The arrays are freed in free_placement_structs() */ |
| |
| int*** temp_blk_pin_from_port_pin = nullptr; |
| unsigned int itype; |
| int iport, iport_pin; |
| int blk_pin_count, num_port_pins, num_ports; |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| /* Allocate and initialize the values to OPEN (-1). */ |
| temp_blk_pin_from_port_pin = (int***)vtr::malloc(device_ctx.logical_block_types.size() * sizeof(int**)); |
| for (itype = 1; itype < device_ctx.logical_block_types.size(); itype++) { |
| num_ports = device_ctx.logical_block_types[itype].pb_type->num_ports; |
| temp_blk_pin_from_port_pin[itype] = (int**)vtr::malloc(num_ports * sizeof(int*)); |
| for (iport = 0; iport < num_ports; iport++) { |
| num_port_pins = device_ctx.logical_block_types[itype].pb_type->ports[iport].num_pins; |
| temp_blk_pin_from_port_pin[itype][iport] = (int*)vtr::malloc(num_port_pins * sizeof(int)); |
| |
| for (iport_pin = 0; iport_pin < num_port_pins; iport_pin++) { |
| temp_blk_pin_from_port_pin[itype][iport][iport_pin] = OPEN; |
| } |
| } |
| } |
| |
| /* Load the values */ |
| /* itype starts from 1 since device_ctx.block_types[0] is the EMPTY_TYPE. */ |
| for (itype = 1; itype < device_ctx.logical_block_types.size(); itype++) { |
| blk_pin_count = 0; |
| num_ports = device_ctx.logical_block_types[itype].pb_type->num_ports; |
| for (iport = 0; iport < num_ports; iport++) { |
| num_port_pins = device_ctx.logical_block_types[itype].pb_type->ports[iport].num_pins; |
| for (iport_pin = 0; iport_pin < num_port_pins; iport_pin++) { |
| temp_blk_pin_from_port_pin[itype][iport][iport_pin] = blk_pin_count; |
| blk_pin_count++; |
| } |
| } |
| } |
| |
| /* Sets the file_scope variables to point at the arrays. */ |
| f_blk_pin_from_port_pin = temp_blk_pin_from_port_pin; |
| } |
| |
| /*************************************************************************************** |
| * Y.G.THIEN |
| * 30 AUG 2012 |
| * |
| * The following functions parses the direct connections' information obtained from * |
| * the arch file. Then, the functions map the block pins indices for all block types * |
| * to the corresponding idirect (the index of the direct connection as specified in * |
| * the arch file) and direct type (whether this pin is a SOURCE or a SINK for the * |
| * direct connection). If a pin is not part of any direct connections, the value * |
| * OPEN (-1) is stored in both entries. * |
| * * |
| * The mapping arrays are freed by the caller. Currently, this mapping is only used to * |
| * load placement macros in place_macro.c * |
| * * |
| ***************************************************************************************/ |
| |
| void parse_direct_pin_name(char* src_string, int line, int* start_pin_index, int* end_pin_index, char* pb_type_name, char* port_name) { |
| /* Parses out the pb_type_name and port_name from the direct passed in. * |
| * If the start_pin_index and end_pin_index is specified, parse them too. * |
| * Return the values parsed by reference. */ |
| |
| char source_string[MAX_STRING_LEN + 1]; |
| char* find_format = nullptr; |
| int ichar, match_count; |
| |
| if (vtr::split(src_string).size() > 1) { |
| VPR_THROW(VPR_ERROR_ARCH, |
| "Only a single port pin range specification allowed for direct connect (was: '%s')", src_string); |
| } |
| |
| // parse out the pb_type and port name, possibly pin_indices |
| find_format = strstr(src_string, "["); |
| if (find_format == nullptr) { |
| /* Format "pb_type_name.port_name" */ |
| *start_pin_index = *end_pin_index = -1; |
| |
| if (strlen(src_string) + 1 <= MAX_STRING_LEN + 1) { |
| strcpy(source_string, src_string); |
| } else { |
| VPR_FATAL_ERROR(VPR_ERROR_ARCH, |
| "Pin name exceeded buffer size of %zu characters", MAX_STRING_LEN + 1); |
| } |
| for (ichar = 0; ichar < (int)(strlen(source_string)); ichar++) { |
| if (source_string[ichar] == '.') |
| source_string[ichar] = ' '; |
| } |
| |
| match_count = sscanf(source_string, "%s %s", pb_type_name, port_name); |
| if (match_count != 2) { |
| VTR_LOG_ERROR( |
| "[LINE %d] Invalid pin - %s, name should be in the format " |
| "\"pb_type_name\".\"port_name\" or \"pb_type_name\".\"port_name[end_pin_index:start_pin_index]\". " |
| "The end_pin_index and start_pin_index can be the same.\n", |
| line, src_string); |
| exit(1); |
| } |
| } else { |
| /* Format "pb_type_name.port_name[end_pin_index:start_pin_index]" */ |
| strcpy(source_string, src_string); |
| for (ichar = 0; ichar < (int)(strlen(source_string)); ichar++) { |
| //Need white space between the components when using %s with |
| //sscanf |
| if (source_string[ichar] == '.') |
| source_string[ichar] = ' '; |
| if (source_string[ichar] == '[') |
| source_string[ichar] = ' '; |
| } |
| |
| match_count = sscanf(source_string, "%s %s %d:%d]", |
| pb_type_name, port_name, |
| end_pin_index, start_pin_index); |
| if (match_count != 4) { |
| VTR_LOG_ERROR( |
| "[LINE %d] Invalid pin - %s, name should be in the format " |
| "\"pb_type_name\".\"port_name\" or \"pb_type_name\".\"port_name[end_pin_index:start_pin_index]\". " |
| "The end_pin_index and start_pin_index can be the same.\n", |
| line, src_string); |
| exit(1); |
| } |
| if (*end_pin_index < 0 || *start_pin_index < 0) { |
| VTR_LOG_ERROR( |
| "[LINE %d] Invalid pin - %s, the pin_index in " |
| "[end_pin_index:start_pin_index] should not be a negative value.\n", |
| line, src_string); |
| exit(1); |
| } |
| if (*end_pin_index < *start_pin_index) { |
| VTR_LOG_ERROR( |
| "[LINE %d] Invalid from_pin - %s, the end_pin_index in " |
| "[end_pin_index:start_pin_index] should not be less than start_pin_index.\n", |
| line, src_string); |
| exit(1); |
| } |
| } |
| } |
| |
| static void mark_direct_of_pins(int start_pin_index, int end_pin_index, int itype, int iport, int** idirect_from_blk_pin, int idirect, int** direct_type_from_blk_pin, int direct_type, int line, char* src_string) { |
| /* Mark the pin entry in idirect_from_blk_pin with idirect and the pin entry in * |
| * direct_type_from_blk_pin with direct_type from start_pin_index to * |
| * end_pin_index. */ |
| |
| int iport_pin, iblk_pin; |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| // Mark pins with indices from start_pin_index to end_pin_index, inclusive |
| for (iport_pin = start_pin_index; iport_pin <= end_pin_index; iport_pin++) { |
| get_blk_pin_from_port_pin(itype, iport, iport_pin, &iblk_pin); |
| |
| //iterate through all segment connections and check if all Fc's are 0 |
| bool all_fcs_0 = true; |
| for (const auto& fc_spec : device_ctx.physical_tile_types[itype].fc_specs) { |
| for (int ipin : fc_spec.pins) { |
| if (iblk_pin == ipin && fc_spec.fc_value > 0) { |
| all_fcs_0 = false; |
| break; |
| } |
| } |
| if (!all_fcs_0) break; |
| } |
| |
| // Check the fc for the pin, direct chain link only if fc == 0 |
| if (all_fcs_0) { |
| idirect_from_blk_pin[itype][iblk_pin] = idirect; |
| |
| // Check whether the pins are marked, errors out if so |
| if (direct_type_from_blk_pin[itype][iblk_pin] != OPEN) { |
| VPR_FATAL_ERROR(VPR_ERROR_ARCH, |
| "[LINE %d] Invalid pin - %s, this pin is in more than one direct connection.\n", |
| line, src_string); |
| } else { |
| direct_type_from_blk_pin[itype][iblk_pin] = direct_type; |
| } |
| } |
| } // Finish marking all the pins |
| } |
| |
| static void mark_direct_of_ports(int idirect, int direct_type, char* pb_type_name, char* port_name, int end_pin_index, int start_pin_index, char* src_string, int line, int** idirect_from_blk_pin, int** direct_type_from_blk_pin) { |
| /* Go through all the ports in all the blocks to find the port that has the same * |
| * name as port_name and belongs to the block type that has the name pb_type_name. * |
| * Then, check that whether start_pin_index and end_pin_index are specified. If * |
| * they are, mark down the pins from start_pin_index to end_pin_index, inclusive. * |
| * Otherwise, mark down all the pins in that port. */ |
| |
| int num_ports, num_port_pins; |
| unsigned int itype; |
| int iport; |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| // Go through all the block types |
| for (itype = 1; itype < device_ctx.logical_block_types.size(); itype++) { |
| // Find blocks with the same pb_type_name |
| if (strcmp(device_ctx.logical_block_types[itype].pb_type->name, pb_type_name) == 0) { |
| num_ports = device_ctx.logical_block_types[itype].pb_type->num_ports; |
| for (iport = 0; iport < num_ports; iport++) { |
| // Find ports with the same port_name |
| if (strcmp(device_ctx.logical_block_types[itype].pb_type->ports[iport].name, port_name) == 0) { |
| num_port_pins = device_ctx.logical_block_types[itype].pb_type->ports[iport].num_pins; |
| |
| // Check whether the end_pin_index is valid |
| if (end_pin_index > num_port_pins) { |
| VTR_LOG_ERROR( |
| "[LINE %d] Invalid pin - %s, the end_pin_index in " |
| "[end_pin_index:start_pin_index] should " |
| "be less than the num_port_pins %d.\n", |
| line, src_string, num_port_pins); |
| exit(1); |
| } |
| |
| // Check whether the pin indices are specified |
| if (start_pin_index >= 0 || end_pin_index >= 0) { |
| mark_direct_of_pins(start_pin_index, end_pin_index, itype, |
| iport, idirect_from_blk_pin, idirect, |
| direct_type_from_blk_pin, direct_type, line, src_string); |
| } else { |
| mark_direct_of_pins(0, num_port_pins - 1, itype, |
| iport, idirect_from_blk_pin, idirect, |
| direct_type_from_blk_pin, direct_type, line, src_string); |
| } |
| } // Do nothing if port_name does not match |
| } // Finish going through all the ports |
| } // Do nothing if pb_type_name does not match |
| } // Finish going through all the blocks |
| } |
| |
| void alloc_and_load_idirect_from_blk_pin(t_direct_inf* directs, int num_directs, int*** idirect_from_blk_pin, int*** direct_type_from_blk_pin) { |
| /* Allocates and loads idirect_from_blk_pin and direct_type_from_blk_pin arrays. * |
| * * |
| * For a bus (multiple bits) direct connection, all the pins in the bus are marked. * |
| * * |
| * idirect_from_blk_pin array allow us to quickly find pins that could be in a * |
| * direct connection. Values stored is the index of the possible direct connection * |
| * as specified in the arch file, OPEN (-1) is stored for pins that could not be * |
| * part of a direct chain conneciton. * |
| * * |
| * direct_type_from_blk_pin array stores the value SOURCE if the pin is the * |
| * from_pin, SINK if the pin is the to_pin in the direct connection as specified in * |
| * the arch file, OPEN (-1) is stored for pins that could not be part of a direct * |
| * chain conneciton. * |
| * * |
| * Stores the pointers to the two 2D arrays in the addresses passed in. * |
| * * |
| * The two arrays are freed by the caller(s). */ |
| |
| int iblk_pin, idirect, num_type_pins; |
| int **temp_idirect_from_blk_pin, **temp_direct_type_from_blk_pin; |
| |
| char to_pb_type_name[MAX_STRING_LEN + 1], to_port_name[MAX_STRING_LEN + 1], |
| from_pb_type_name[MAX_STRING_LEN + 1], from_port_name[MAX_STRING_LEN + 1]; |
| int to_start_pin_index = -1, to_end_pin_index = -1; |
| int from_start_pin_index = -1, from_end_pin_index = -1; |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| /* Allocate and initialize the values to OPEN (-1). */ |
| temp_idirect_from_blk_pin = (int**)vtr::malloc(device_ctx.logical_block_types.size() * sizeof(int*)); |
| temp_direct_type_from_blk_pin = (int**)vtr::malloc(device_ctx.logical_block_types.size() * sizeof(int*)); |
| for (const auto& type : device_ctx.logical_block_types) { |
| int itype = type.index; |
| num_type_pins = physical_tile_type(&type)->num_pins; |
| |
| temp_idirect_from_blk_pin[itype] = (int*)vtr::malloc(num_type_pins * sizeof(int)); |
| temp_direct_type_from_blk_pin[itype] = (int*)vtr::malloc(num_type_pins * sizeof(int)); |
| |
| /* Initialize values to OPEN */ |
| for (iblk_pin = 0; iblk_pin < num_type_pins; iblk_pin++) { |
| temp_idirect_from_blk_pin[itype][iblk_pin] = OPEN; |
| temp_direct_type_from_blk_pin[itype][iblk_pin] = OPEN; |
| } |
| } |
| |
| /* Load the values */ |
| // Go through directs and find pins with possible direct connections |
| for (idirect = 0; idirect < num_directs; idirect++) { |
| // Parse out the pb_type and port name, possibly pin_indices from from_pin |
| parse_direct_pin_name(directs[idirect].from_pin, directs[idirect].line, |
| &from_end_pin_index, &from_start_pin_index, from_pb_type_name, from_port_name); |
| |
| // Parse out the pb_type and port name, possibly pin_indices from to_pin |
| parse_direct_pin_name(directs[idirect].to_pin, directs[idirect].line, |
| &to_end_pin_index, &to_start_pin_index, to_pb_type_name, to_port_name); |
| |
| /* Now I have all the data that I need, I could go through all the block pins * |
| * in all the blocks to find all the pins that could have possible direct * |
| * connections. Mark all down all those pins with the idirect the pins belong * |
| * to and whether it is a source or a sink of the direct connection. */ |
| |
| // Find blocks with the same name as from_pb_type_name and from_port_name |
| mark_direct_of_ports(idirect, SOURCE, from_pb_type_name, from_port_name, |
| from_end_pin_index, from_start_pin_index, directs[idirect].from_pin, |
| directs[idirect].line, |
| temp_idirect_from_blk_pin, temp_direct_type_from_blk_pin); |
| |
| // Then, find blocks with the same name as to_pb_type_name and from_port_name |
| mark_direct_of_ports(idirect, SINK, to_pb_type_name, to_port_name, |
| to_end_pin_index, to_start_pin_index, directs[idirect].to_pin, |
| directs[idirect].line, |
| temp_idirect_from_blk_pin, temp_direct_type_from_blk_pin); |
| |
| } // Finish going through all the directs |
| |
| /* Returns the pointer to the 2D arrays by reference. */ |
| *idirect_from_blk_pin = temp_idirect_from_blk_pin; |
| *direct_type_from_blk_pin = temp_direct_type_from_blk_pin; |
| } |
| |
| /* |
| * this function is only called by print_switch_usage() |
| * at the point of this function call, every switch type / fanin combination |
| * has a unique index. |
| * but for switch usage analysis, we need to convert the index back to the |
| * type / fanin combination |
| */ |
| static int convert_switch_index(int* switch_index, int* fanin) { |
| if (*switch_index == -1) |
| return 1; |
| |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| for (int iswitch = 0; iswitch < device_ctx.num_arch_switches; iswitch++) { |
| for (auto itr = device_ctx.switch_fanin_remap[iswitch].begin(); itr != device_ctx.switch_fanin_remap[iswitch].end(); itr++) { |
| if (itr->second == *switch_index) { |
| *switch_index = iswitch; |
| *fanin = itr->first; |
| return 0; |
| } |
| } |
| } |
| *switch_index = -1; |
| *fanin = -1; |
| VTR_LOG("\n\nerror converting switch index ! \n\n"); |
| return -1; |
| } |
| |
| /* |
| * print out number of usage for every switch (type / fanin combination) |
| * (referring to rr_graph.c: alloc_rr_switch_inf()) |
| * NOTE: to speed up this function, for XXX uni-directional arch XXX, the most efficient |
| * way is to change the device_ctx.rr_nodes data structure (let it store the inward switch index, |
| * instead of outward switch index list): --> instead of using a nested loop of |
| * for (inode in rr_nodes) { |
| * for (iedges in edges) { |
| * get switch type; |
| * get fanin; |
| * } |
| * } |
| * as is done in rr_graph.c: alloc_rr_switch_inf() |
| * we can just use a single loop |
| * for (inode in rr_nodes) { |
| * get switch type of inode; |
| * get fanin of inode; |
| * } |
| * now since device_ctx.rr_nodes does not contain the switch type inward to the current node, |
| * we have to use an extra loop to setup the information of inward switch first. |
| */ |
| void print_switch_usage() { |
| auto& device_ctx = g_vpr_ctx.device(); |
| |
| if (device_ctx.switch_fanin_remap.empty()) { |
| VTR_LOG_WARN("Cannot print switch usage stats: device_ctx.switch_fanin_remap is empty\n"); |
| return; |
| } |
| std::map<int, int>* switch_fanin_count; |
| std::map<int, float>* switch_fanin_delay; |
| switch_fanin_count = new std::map<int, int>[device_ctx.num_arch_switches]; |
| switch_fanin_delay = new std::map<int, float>[device_ctx.num_arch_switches]; |
| // a node can have multiple inward switches, so |
| // map key: switch index; map value: count (fanin) |
| std::map<int, int>* inward_switch_inf = new std::map<int, int>[device_ctx.rr_nodes.size()]; |
| for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); inode++) { |
| const t_rr_node& from_node = device_ctx.rr_nodes[inode]; |
| int num_edges = from_node.num_edges(); |
| for (int iedge = 0; iedge < num_edges; iedge++) { |
| int switch_index = from_node.edge_switch(iedge); |
| int to_node_index = from_node.edge_sink_node(iedge); |
| // Assumption: suppose for a L4 wire (bi-directional): ----+----+----+----, it can be driven from any point (0, 1, 2, 3). |
| // physically, the switch driving from point 1 & 3 should be the same. But we will assign then different switch |
| // index; or there is no way to differentiate them after abstracting a 2D wire into a 1D node |
| if (inward_switch_inf[to_node_index].count(switch_index) == 0) |
| inward_switch_inf[to_node_index][switch_index] = 0; |
| //VTR_ASSERT(from_node.type != OPIN); |
| inward_switch_inf[to_node_index][switch_index]++; |
| } |
| } |
| for (size_t inode = 0; inode < device_ctx.rr_nodes.size(); inode++) { |
| std::map<int, int>::iterator itr; |
| for (itr = inward_switch_inf[inode].begin(); itr != inward_switch_inf[inode].end(); itr++) { |
| int switch_index = itr->first; |
| int fanin = itr->second; |
| float Tdel = device_ctx.rr_switch_inf[switch_index].Tdel; |
| int status = convert_switch_index(&switch_index, &fanin); |
| if (status == -1) { |
| delete[] switch_fanin_count; |
| delete[] switch_fanin_delay; |
| delete[] inward_switch_inf; |
| return; |
| } |
| if (switch_fanin_count[switch_index].count(fanin) == 0) { |
| switch_fanin_count[switch_index][fanin] = 0; |
| } |
| switch_fanin_count[switch_index][fanin]++; |
| switch_fanin_delay[switch_index][fanin] = Tdel; |
| } |
| } |
| VTR_LOG("\n=============== switch usage stats ===============\n"); |
| for (int iswitch = 0; iswitch < device_ctx.num_arch_switches; iswitch++) { |
| char* s_name = device_ctx.arch_switch_inf[iswitch].name; |
| float s_area = device_ctx.arch_switch_inf[iswitch].mux_trans_size; |
| VTR_LOG(">>>>> switch index: %d, name: %s, mux trans size: %g\n", iswitch, s_name, s_area); |
| |
| std::map<int, int>::iterator itr; |
| for (itr = switch_fanin_count[iswitch].begin(); itr != switch_fanin_count[iswitch].end(); itr++) { |
| VTR_LOG("\t\tnumber of fanin: %d", itr->first); |
| VTR_LOG("\t\tnumber of wires driven by this switch: %d", itr->second); |
| VTR_LOG("\t\tTdel: %g\n", switch_fanin_delay[iswitch][itr->first]); |
| } |
| } |
| VTR_LOG("\n==================================================\n\n"); |
| delete[] switch_fanin_count; |
| delete[] switch_fanin_delay; |
| delete[] inward_switch_inf; |
| } |
| |
| /* |
| * Motivation: |
| * to see what portion of long wires are utilized |
| * potentially a good measure for router look ahead quality |
| */ |
| /* |
| * void print_usage_by_wire_length() { |
| * map<int, int> used_wire_count; |
| * map<int, int> total_wire_count; |
| * auto& device_ctx = g_vpr_ctx.device(); |
| * for (int inode = 0; inode < device_ctx.rr_nodes.size(); inode++) { |
| * if (device_ctx.rr_nodes[inode].type() == CHANX || rr_node[inode].type() == CHANY) { |
| * //int length = abs(device_ctx.rr_nodes[inode].get_xhigh() + rr_node[inode].get_yhigh() |
| * // - device_ctx.rr_nodes[inode].get_xlow() - rr_node[inode].get_ylow()); |
| * int length = device_ctx.rr_nodes[inode].get_length(); |
| * if (rr_node_route_inf[inode].occ() > 0) { |
| * if (used_wire_count.count(length) == 0) |
| * used_wire_count[length] = 0; |
| * used_wire_count[length] ++; |
| * } |
| * if (total_wire_count.count(length) == 0) |
| * total_wire_count[length] = 0; |
| * total_wire_count[length] ++; |
| * } |
| * } |
| * int total_wires = 0; |
| * map<int, int>::iterator itr; |
| * for (itr = total_wire_count.begin(); itr != total_wire_count.end(); itr++) { |
| * total_wires += itr->second; |
| * } |
| * VTR_LOG("\n\t-=-=-=-=-=-=-=-=-=-=- wire usage stats -=-=-=-=-=-=-=-=-=-=-\n"); |
| * for (itr = total_wire_count.begin(); itr != total_wire_count.end(); itr++) |
| * VTR_LOG("\ttotal number: wire of length %d, ratio to all length of wires: %g\n", itr->first, ((float)itr->second) / total_wires); |
| * for (itr = used_wire_count.begin(); itr != used_wire_count.end(); itr++) { |
| * float ratio_to_same_type_total = ((float)itr->second) / total_wire_count[itr->first]; |
| * float ratio_to_all_type_total = ((float)itr->second) / total_wires; |
| * VTR_LOG("\t\tratio to same type of wire: %g\tratio to all types of wire: %g\n", ratio_to_same_type_total, ratio_to_all_type_total); |
| * } |
| * VTR_LOG("\n\t-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n"); |
| * used_wire_count.clear(); |
| * total_wire_count.clear(); |
| * } |
| */ |
| |
| void place_sync_external_block_connections(ClusterBlockId iblk) { |
| auto& cluster_ctx = g_vpr_ctx.mutable_clustering(); |
| auto& place_ctx = g_vpr_ctx.mutable_placement(); |
| VTR_ASSERT_MSG(place_ctx.block_locs[iblk].nets_and_pins_synced_to_z_coordinate == false, "Block net and pins must not be already synced"); |
| |
| auto type = physical_tile_type(iblk); |
| VTR_ASSERT(type->num_pins % type->capacity == 0); |
| int max_num_block_pins = type->num_pins / type->capacity; |
| /* Logical location and physical location is offset by z * max_num_block_pins */ |
| |
| auto& clb_nlist = cluster_ctx.clb_nlist; |
| for (auto pin : clb_nlist.block_pins(iblk)) { |
| int orig_phys_pin_index = clb_nlist.pin_physical_index(pin); |
| int new_phys_pin_index = orig_phys_pin_index + place_ctx.block_locs[iblk].loc.z * max_num_block_pins; |
| clb_nlist.set_pin_physical_index(pin, new_phys_pin_index); |
| } |
| |
| //Mark the block as synced |
| place_ctx.block_locs[iblk].nets_and_pins_synced_to_z_coordinate = true; |
| } |
| |
| int max_pins_per_grid_tile() { |
| auto& device_ctx = g_vpr_ctx.device(); |
| int max_pins = 0; |
| for (auto& type : device_ctx.physical_tile_types) { |
| int pins_per_grid_tile = type.num_pins / (type.width * type.height); |
| //Use the maximum number of pins normalized by block area |
| max_pins = std::max(max_pins, pins_per_grid_tile); |
| } |
| return max_pins; |
| } |
| |
| void pretty_print_uint(const char* prefix, size_t value, int num_digits, int scientific_precision) { |
| //Print as integer if it will fit in the width, other wise scientific |
| if (value <= std::pow(10, num_digits) - 1) { |
| //Direct |
| VTR_LOG("%s%*zu", prefix, num_digits, value); |
| } else { |
| //Scientific |
| VTR_LOG("%s%#*.*g", prefix, num_digits, scientific_precision, float(value)); |
| } |
| } |
| |
| void pretty_print_float(const char* prefix, double value, int num_digits, int scientific_precision) { |
| //Print as float if it will fit in the width, other wise scientific |
| |
| //How many whole digits are there in non-scientific style? |
| size_t whole_digits = num_digits - scientific_precision - 1; //-1 for decimal point |
| if (value <= std::pow(10, whole_digits) - 1) { |
| //Direct |
| VTR_LOG("%s%*.*f", prefix, num_digits, scientific_precision, value); |
| } else { |
| //Scientific |
| VTR_LOG("%s%#*.*g", prefix, num_digits, scientific_precision + 1, value); |
| } |
| } |