diff --git a/libs/libarchfpga/src/arch_util.cpp b/libs/libarchfpga/src/arch_util.cpp index 64c16bbd093..4932d9fb2dc 100644 --- a/libs/libarchfpga/src/arch_util.cpp +++ b/libs/libarchfpga/src/arch_util.cpp @@ -555,9 +555,9 @@ t_port* findPortByName(const char* name, t_pb_type* pb_type, int* high_index, in return port; } -t_physical_tile_type SetupEmptyPhysicalType() { +t_physical_tile_type get_empty_physical_type(std::string name) { t_physical_tile_type type; - type.name = vtr::strdup("EMPTY"); + type.name = vtr::strdup(name.c_str()); type.num_pins = 0; type.width = 1; type.height = 1; @@ -573,9 +573,9 @@ t_physical_tile_type SetupEmptyPhysicalType() { return type; } -t_logical_block_type SetupEmptyLogicalType() { +t_logical_block_type get_empty_logical_type(std::string name) { t_logical_block_type type; - type.name = vtr::strdup("EMPTY"); + type.name = vtr::strdup(name.c_str()); type.pb_type = nullptr; return type; @@ -1392,5 +1392,208 @@ const t_pin_to_pin_annotation* find_combinational_annotation(const t_pb_type* pb } } } + return nullptr; } + +void link_physical_logical_types(std::vector& PhysicalTileTypes, + std::vector& LogicalBlockTypes) { + for (auto& physical_tile : PhysicalTileTypes) { + if (physical_tile.index == EMPTY_TYPE_INDEX) continue; + + auto eq_sites_set = get_equivalent_sites_set(&physical_tile); + auto equivalent_sites = std::vector(eq_sites_set.begin(), eq_sites_set.end()); + + auto criteria = [&physical_tile](const t_logical_block_type* lhs, const t_logical_block_type* rhs) { + int num_pins = physical_tile.num_inst_pins; + + int lhs_num_logical_pins = lhs->pb_type->num_pins; + int rhs_num_logical_pins = rhs->pb_type->num_pins; + + int lhs_diff_num_pins = num_pins - lhs_num_logical_pins; + int rhs_diff_num_pins = num_pins - rhs_num_logical_pins; + + return lhs_diff_num_pins < rhs_diff_num_pins; + }; + + std::sort(equivalent_sites.begin(), equivalent_sites.end(), criteria); + + for (auto& logical_block : LogicalBlockTypes) { + for (auto site : equivalent_sites) { + if (0 == strcmp(logical_block.name, site->pb_type->name)) { + logical_block.equivalent_tiles.push_back(&physical_tile); + break; + } + } + } + } + + for (auto& logical_block : LogicalBlockTypes) { + if (logical_block.index == EMPTY_TYPE_INDEX) continue; + + auto& equivalent_tiles = logical_block.equivalent_tiles; + + if ((int)equivalent_tiles.size() <= 0) { + archfpga_throw(__FILE__, __LINE__, + "Logical Block %s does not have any equivalent tiles.\n", logical_block.name); + } + + std::unordered_map ignored_pins_check_map; + std::unordered_map global_pins_check_map; + + auto criteria = [&logical_block](const t_physical_tile_type* lhs, const t_physical_tile_type* rhs) { + int num_logical_pins = logical_block.pb_type->num_pins; + + int lhs_num_pins = lhs->num_inst_pins; + int rhs_num_pins = rhs->num_inst_pins; + + int lhs_diff_num_pins = lhs_num_pins - num_logical_pins; + int rhs_diff_num_pins = rhs_num_pins - num_logical_pins; + + return lhs_diff_num_pins < rhs_diff_num_pins; + }; + + std::sort(equivalent_tiles.begin(), equivalent_tiles.end(), criteria); + + for (int pin = 0; pin < logical_block.pb_type->num_pins; pin++) { + for (auto& tile : equivalent_tiles) { + auto direct_maps = tile->tile_block_pin_directs_map.at(logical_block.index); + + for (auto& sub_tile : tile->sub_tiles) { + auto equiv_sites = sub_tile.equivalent_sites; + if (std::find(equiv_sites.begin(), equiv_sites.end(), &logical_block) == equiv_sites.end()) { + continue; + } + + auto direct_map = direct_maps.at(sub_tile.index); + + auto result = direct_map.find(t_logical_pin(pin)); + if (result == direct_map.end()) { + archfpga_throw(__FILE__, __LINE__, + "Logical pin %d not present in pin mapping between Tile %s and Block %s.\n", + pin, tile->name, logical_block.name); + } + + int sub_tile_pin_index = result->second.pin; + int phy_index = sub_tile.sub_tile_to_tile_pin_indices[sub_tile_pin_index]; + + bool is_ignored = tile->is_ignored_pin[phy_index]; + bool is_global = tile->is_pin_global[phy_index]; + + auto ignored_result = ignored_pins_check_map.insert(std::pair(pin, is_ignored)); + if (!ignored_result.second && ignored_result.first->second != is_ignored) { + archfpga_throw(__FILE__, __LINE__, + "Physical Tile %s has a different value for the ignored pin (physical pin: %d, logical pin: %d) " + "different from the corresponding pins of the other equivalent site %s\n.", + tile->name, phy_index, pin, logical_block.name); + } + + auto global_result = global_pins_check_map.insert(std::pair(pin, is_global)); + if (!global_result.second && global_result.first->second != is_global) { + archfpga_throw(__FILE__, __LINE__, + "Physical Tile %s has a different value for the global pin (physical pin: %d, logical pin: %d) " + "different from the corresponding pins of the other equivalent sites\n.", + tile->name, phy_index, pin); + } + } + } + } + } +} + +/* Sets up the pin classes for the type. */ +void setup_pin_classes(t_physical_tile_type* type) { + int i, k; + int pin_count; + int num_class; + + for (i = 0; i < type->num_pins; i++) { + type->pin_class.push_back(OPEN); + type->is_ignored_pin.push_back(true); + type->is_pin_global.push_back(true); + } + + pin_count = 0; + + t_class_range class_range; + + /* Equivalent pins share the same class, non-equivalent pins belong to different pin classes */ + for (auto& sub_tile : type->sub_tiles) { + int capacity = sub_tile.capacity.total(); + class_range.low = type->class_inf.size(); + class_range.high = class_range.low - 1; + for (i = 0; i < capacity; ++i) { + for (const auto& port : sub_tile.ports) { + if (port.equivalent != PortEquivalence::NONE) { + t_class class_inf; + num_class = (int)type->class_inf.size(); + class_inf.num_pins = port.num_pins; + class_inf.equivalence = port.equivalent; + + if (port.type == IN_PORT) { + class_inf.type = RECEIVER; + } else { + VTR_ASSERT(port.type == OUT_PORT); + class_inf.type = DRIVER; + } + + for (k = 0; k < port.num_pins; ++k) { + class_inf.pinlist.push_back(pin_count); + type->pin_class[pin_count] = num_class; + // clock pins and other specified global ports are initially specified + // as ignored pins (i.e. connections are not created in the rr_graph and + // nets connected to the port are ignored as well). + type->is_ignored_pin[pin_count] = port.is_clock || port.is_non_clock_global; + // clock pins and other specified global ports are flaged as global + type->is_pin_global[pin_count] = port.is_clock || port.is_non_clock_global; + + if (port.is_clock) { + type->clock_pin_indices.push_back(pin_count); + } + + pin_count++; + } + + type->class_inf.push_back(class_inf); + class_range.high++; + } else if (port.equivalent == PortEquivalence::NONE) { + for (k = 0; k < port.num_pins; ++k) { + t_class class_inf; + num_class = (int)type->class_inf.size(); + class_inf.num_pins = 1; + class_inf.pinlist.push_back(pin_count); + class_inf.equivalence = port.equivalent; + + if (port.type == IN_PORT) { + class_inf.type = RECEIVER; + } else { + VTR_ASSERT(port.type == OUT_PORT); + class_inf.type = DRIVER; + } + + type->pin_class[pin_count] = num_class; + // clock pins and other specified global ports are initially specified + // as ignored pins (i.e. connections are not created in the rr_graph and + // nets connected to the port are ignored as well). + type->is_ignored_pin[pin_count] = port.is_clock || port.is_non_clock_global; + // clock pins and other specified global ports are flaged as global + type->is_pin_global[pin_count] = port.is_clock || port.is_non_clock_global; + + if (port.is_clock) { + type->clock_pin_indices.push_back(pin_count); + } + + pin_count++; + + type->class_inf.push_back(class_inf); + class_range.high++; + } + } + } + } + + type->sub_tiles[sub_tile.index].class_range = class_range; + } + + VTR_ASSERT(pin_count == type->num_pins); +} diff --git a/libs/libarchfpga/src/arch_util.h b/libs/libarchfpga/src/arch_util.h index b67240b82d5..d5519f5684c 100644 --- a/libs/libarchfpga/src/arch_util.h +++ b/libs/libarchfpga/src/arch_util.h @@ -60,8 +60,8 @@ void free_type_descriptors(std::vector& type_descriptors); t_port* findPortByName(const char* name, t_pb_type* pb_type, int* high_index, int* low_index); -t_physical_tile_type SetupEmptyPhysicalType(); -t_logical_block_type SetupEmptyLogicalType(); +t_physical_tile_type get_empty_physical_type(std::string name); +t_logical_block_type get_empty_logical_type(std::string name); std::unordered_set get_equivalent_sites_set(t_physical_tile_type_ptr type); @@ -100,4 +100,8 @@ bool pb_type_contains_blif_model(const t_pb_type* pb_type, const std::string& bl const t_pin_to_pin_annotation* find_sequential_annotation(const t_pb_type* pb_type, const t_model_ports* port, enum e_pin_to_pin_delay_annotations annot_type); const t_pin_to_pin_annotation* find_combinational_annotation(const t_pb_type* pb_type, std::string in_port, std::string out_port); +void link_physical_logical_types(std::vector& PhysicalTileTypes, + std::vector& LogicalBlockTypes); + +void setup_pin_classes(t_physical_tile_type* type); #endif diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index ebfe950afc9..41bf53eb1d4 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -1738,6 +1738,20 @@ struct t_lut_cell { std::vector inputs; }; +struct t_lut_bel { + std::string name; + + std::vector input_pins; + std::string output_pin; +}; + +struct t_package_pin { + std::string name; + + std::string site_name; + std::string bel_name; +}; + /* Detailed routing architecture */ struct t_arch { mutable vtr::string_internment strings; @@ -1787,6 +1801,11 @@ struct t_arch { // Luts std::vector lut_cells; + std::vector lut_bels; + + // Package pins + // TODO: add possibility to have multiple packages + std::vector pad_bels; //The name of the switch used for the input connection block (i.e. to //connect routing tracks to block pins). diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index fffd3e864d2..c0cd0dba881 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -11,6 +11,7 @@ #include "vtr_assert.h" #include "vtr_digest.h" #include "vtr_log.h" +#include "vtr_memory.h" #include "vtr_util.h" #include "arch_check.h" @@ -34,6 +35,8 @@ using namespace DeviceResources; using namespace LogicalNetlist; using namespace capnp; +/****************** Utility functions ******************/ + /** * @brief The FPGA interchange timing model includes three different corners (min, typ and max) for each of the two * speed_models (slow and fast). @@ -110,22 +113,115 @@ static float get_corner_value(Device::CornerModel::Reader model, const char* spe return 0.; } +static t_model_ports* get_model_port(t_arch* arch, std::string model, std::string port) { + for (t_model* m : {arch->models, arch->model_library}) { + for (; m != nullptr; m = m->next) { + if (std::string(m->name) != model) + continue; + + for (t_model_ports* p : {m->inputs, m->outputs}) + for (; p != nullptr; p = p->next) + if (std::string(p->name) == port) + return p; + } + } + + archfpga_throw(__FILE__, __LINE__, + "Could not find model port: %s (%s)\n", port.c_str(), model.c_str()); +} + +static t_model* get_model(t_arch* arch, std::string model) { + for (t_model* m : {arch->models, arch->model_library}) + for (; m != nullptr; m = m->next) + if (std::string(m->name) == model) + return m; + + archfpga_throw(__FILE__, __LINE__, + "Could not find model: %s\n", model.c_str()); +} + +template +static T* get_type_by_name(const char* type_name, std::vector& types) { + for (auto& type : types) { + if (0 == strcmp(type.name, type_name)) { + return &type; + } + } + + archfpga_throw(__FILE__, __LINE__, + "Could not find type: %s\n", type_name); +} + +static t_port get_generic_port(t_arch* arch, + t_pb_type* pb_type, + PORTS dir, + std::string name, + std::string model = "", + int num_pins = 1) { + t_port port; + port.parent_pb_type = pb_type; + port.name = vtr::strdup(name.c_str()); + port.num_pins = num_pins; + port.index = 0; + port.absolute_first_pin_index = 0; + port.port_index_by_type = 0; + port.equivalent = PortEquivalence::NONE; + port.type = dir; + port.is_clock = false; + port.model_port = nullptr; + port.port_class = vtr::strdup(nullptr); + port.port_power = (t_port_power*)vtr::calloc(1, sizeof(t_port_power)); + + if (!model.empty()) + port.model_port = get_model_port(arch, model, name); + + return port; +} + +/****************** End Utility functions ******************/ + struct ArchReader { public: - ArchReader(t_arch* arch, Device::Reader& arch_reader, const char* arch_file, std::vector& phys_types, std::vector& logical_types) + ArchReader(t_arch* arch, + Device::Reader& arch_reader, + const char* arch_file, + std::vector& phys_types, + std::vector& logical_types) : arch_(arch) , arch_file_(arch_file) , ar_(arch_reader) , ptypes_(phys_types) , ltypes_(logical_types) { set_arch_file_name(arch_file); + + for (auto cell_bel : ar_.getCellBelMap()) { + auto name = str(cell_bel.getCell()); + for (auto site_bels : cell_bel.getCommonPins()[0].getSiteTypes()) { + auto site_type = str(site_bels.getSiteType()); + for (auto bel : site_bels.getBels()) { + auto bel_name = str(bel); + std::pair key(site_type, bel_name); + bel_cell_mapping_[key].push_back(name); + } + } + } } void read_arch() { + // Preprocess arch information process_luts(); + process_package_pins(); + process_models(); process_device(); + process_blocks(); + process_tiles(); + link_physical_logical_types(ptypes_, ltypes_); + + SyncModelsPbTypes(arch_, ltypes_); + check_models(arch_); + process_layout(); process_switches(); process_segments(); @@ -140,6 +236,152 @@ struct ArchReader { t_default_fc_spec default_fc_; + // siteTypeName, belName , list of cell names + std::map, std::vector> bel_cell_mapping_; + + // Utils + std::string str(int idx) { + return std::string(ar_.getStrList()[idx].cStr()); + } + + int get_bel_type_count(Device::SiteType::Reader& site, Device::BELCategory category) { + int count = 0; + for (auto bel : site.getBels()) + if (bel.getCategory() == category) + count++; + + return count; + } + + Device::BEL::Reader get_bel_reader(Device::SiteType::Reader& site, std::string bel_name) { + for (auto bel : site.getBels()) { + if (str(bel.getName()) == bel_name) + return bel; + } + VTR_ASSERT(0); + } + + std::string get_ic_prefix(Device::SiteType::Reader& site, Device::BEL::Reader& bel) { + return bel.getCategory() == Device::BELCategory::SITE_PORT ? str(site.getName()) : str(bel.getName()); + } + + std::unordered_map> get_ics(Device::SiteType::Reader& site) { + // dictionary: + // - key: interconnect name + // - value: (inputs string, outputs string, interconnect type) + std::unordered_map> ics; + for (auto wire : site.getSiteWires()) { + std::string wire_name = str(wire.getName()); + + // pin name, bel name + std::tuple out_pin; + bool is_mux = false; + for (auto pin : wire.getPins()) { + auto bel_pin = site.getBelPins()[pin]; + auto bel = get_bel_reader(site, str(bel_pin.getBel())); + auto bel_name = get_ic_prefix(site, bel); + + bool is_output = bel_pin.getDir() == LogicalNetlist::Netlist::Direction::OUTPUT; + if (is_output) { + VTR_ASSERT(std::get<1>(out_pin).empty()); + out_pin = std::make_tuple(pin, str(bel_pin.getName()), bel_name); + is_mux = bel.getCategory() == Device::BELCategory::ROUTING; + } + } + VTR_ASSERT(!std::get<1>(out_pin).empty()); + + // Stores all output BELs connected to the same out_pin + std::string pad_bel_name; + std::string pad_bel_pin_name; + bool is_pad = false; + for (auto pin : wire.getPins()) { + if (pin == std::get<0>(out_pin)) + continue; + + auto bel_pin = site.getBelPins()[pin]; + std::string out_bel_pin_name = str(bel_pin.getName()); + + auto bel = get_bel_reader(site, str(bel_pin.getBel())); + auto out_bel_name = get_ic_prefix(site, bel); + + for (auto pad_bel : arch_->pad_bels) { + is_pad = pad_bel.bel_name == out_bel_name || is_pad; + pad_bel_name = pad_bel.bel_name == out_bel_name ? out_bel_name : pad_bel_name; + pad_bel_pin_name = pad_bel.bel_name == out_bel_name ? out_bel_pin_name : pad_bel_pin_name; + } + } + + for (auto pin : wire.getPins()) { + if (pin == std::get<0>(out_pin)) + continue; + + auto bel_pin = site.getBelPins()[pin]; + std::string out_bel_pin_name = str(bel_pin.getName()); + + auto bel = get_bel_reader(site, str(bel_pin.getBel())); + auto out_bel_name = get_ic_prefix(site, bel); + + auto in_bel_name = std::get<2>(out_pin); + auto in_bel_pin_name = std::get<1>(out_pin); + + std::string ostr = out_bel_name + "." + out_bel_pin_name; + std::string istr = in_bel_name + "." + in_bel_pin_name; + + std::string inputs; + std::string outputs; + e_interconnect ic_type; + if (is_mux) { + auto ic_name = in_bel_name; + auto res = ics.emplace(ic_name, std::make_tuple(std::string(), ostr, MUX_INTERC, false)); + + if (!res.second) { + std::tie(inputs, outputs, ic_type, std::ignore) = res.first->second; + outputs += " " + ostr; + res.first->second = std::make_tuple(inputs, outputs, ic_type, false); + } + } else if (bel.getCategory() == Device::BELCategory::ROUTING) { + auto ic_name = str(bel.getName()); + auto res = ics.emplace(ic_name, std::make_tuple(istr, std::string(), MUX_INTERC, false)); + + if (!res.second) { + std::tie(inputs, outputs, ic_type, std::ignore) = res.first->second; + inputs += " " + istr; + res.first->second = std::make_tuple(inputs, outputs, ic_type, false); + } + } else { + auto ic_name = wire_name + "_" + out_bel_pin_name; + if (is_pad && bel.getCategory() == Device::BELCategory::LOGIC) { + if (out_bel_name == pad_bel_name) + ostr += "_in"; + else { // Create new wire to connect PAD output to the BELs input + ic_name = wire_name + "_" + pad_bel_pin_name + "_out"; + istr = pad_bel_name + "." + pad_bel_pin_name + "_out"; + } + } + + auto res = ics.emplace(ic_name, std::make_tuple(istr, ostr, DIRECT_INTERC, is_pad)); + + if (!res.second) { + std::tie(inputs, outputs, ic_type, std::ignore) = res.first->second; + if (inputs.empty()) + inputs = istr; + else + inputs += " " + istr; + + if (outputs.empty()) + outputs = ostr; + else + outputs += " " + ostr; + + res.first->second = std::make_tuple(inputs, outputs, ic_type, is_pad); + } + } + } + } + + return ics; + } + // Model processing void process_models() { // Populate the common library, namely .inputs, .outputs, .names, .latches @@ -152,16 +394,25 @@ struct ArchReader { int model_index = NUM_MODELS_IN_LIBRARY; arch_->models = nullptr; - auto strList = ar_.getStrList(); auto primLib = ar_.getPrimLibs(); for (auto primitive : primLib.getCellDecls()) { - if (std::string(strList[primitive.getLib()]) == std::string("primitives")) { + if (str(primitive.getLib()) == std::string("primitives")) { + std::string prim_name = str(primitive.getName()); + + bool is_lut = false; + for (auto lut_cell : arch_->lut_cells) + is_lut = lut_cell.name == prim_name || is_lut; + + if (is_lut) + continue; + try { temp = new t_model; temp->index = model_index++; temp->never_prune = true; - temp->name = vtr::strdup(std::string(strList[primitive.getName()]).c_str()); + temp->name = vtr::strdup(str(primitive.getName()).c_str()); + ret_map_name = model_name_map.insert(std::pair(temp->name, 0)); if (!ret_map_name.second) { archfpga_throw(arch_file_, __LINE__, @@ -182,11 +433,9 @@ struct ArchReader { arch_->models = temp; } } - return; } void process_model_ports(t_model* model, Netlist::CellDeclaration::Reader primitive) { - auto strList = ar_.getStrList(); auto primLib = ar_.getPrimLibs(); auto portList = primLib.getPortList(); @@ -210,7 +459,7 @@ struct ArchReader { } t_model_ports* model_port = new t_model_ports; model_port->dir = dir; - model_port->name = vtr::strdup(std::string(strList[port.getName()]).c_str()); + model_port->name = vtr::strdup(str(port.getName()).c_str()); // TODO: add parsing of clock port types when the interchange schema allows for it: // https://github.com/chipsalliance/fpga-interchange-schema/issues/66 @@ -254,6 +503,571 @@ struct ArchReader { } } + // Complex Blocks + void process_blocks() { + auto siteTypeList = ar_.getSiteTypeList(); + + int index = 0; + auto EMPTY = get_empty_logical_type(std::string("NULL")); + EMPTY.index = index; + ltypes_.push_back(EMPTY); + + for (auto site : siteTypeList) { + t_logical_block_type ltype; + + std::string name = str(site.getName()); + + // Check for duplicates + auto is_duplicate = [name](t_logical_block_type l) { return std::string(l.name) == name; }; + VTR_ASSERT(std::find_if(ltypes_.begin(), ltypes_.end(), is_duplicate) == ltypes_.end()); + + ltype.name = vtr::strdup(name.c_str()); + ltype.index = ++index; + + auto pb_type = new t_pb_type; + ltype.pb_type = pb_type; + + pb_type->name = vtr::strdup(name.c_str()); + pb_type->num_pb = 1; + process_block_ports(pb_type, name, site); + + // Process modes (for simplicity, only the default mode is allowed for the time being) + pb_type->num_modes = 1; + pb_type->modes = new t_mode[pb_type->num_modes]; + + auto bels = site.getBels(); + auto mode = &pb_type->modes[0]; + mode->parent_pb_type = pb_type; + mode->index = 0; + mode->name = vtr::strdup("default"); + mode->disable_packing = false; + + int bel_count = get_bel_type_count(site, Device::BELCategory::LOGIC); + mode->num_pb_type_children = bel_count; + mode->pb_type_children = new t_pb_type[bel_count]; + + int count = 0; + for (auto bel : bels) { + if (bel.getCategory() != Device::BELCategory::LOGIC) + continue; + + auto bel_name = str(bel.getName()); + std::pair key(name, bel_name); + + auto cell_name = bel_name; + + if (bel_cell_mapping_.find(key) != bel_cell_mapping_.end()) { + VTR_ASSERT(bel_cell_mapping_[key].size() == 1); + cell_name = bel_cell_mapping_[key][0]; + } + + auto leaf_pb_type = new t_pb_type; + leaf_pb_type->name = vtr::strdup(bel_name.c_str()); + leaf_pb_type->num_pb = 1; + leaf_pb_type->parent_mode = mode; + + // TODO: fix this to make it dynamic. This will need the usage of CellBelMapping + + auto find_lut = [cell_name](t_lut_cell l) { return l.name == cell_name; }; + bool is_lut = std::find_if(arch_->lut_cells.begin(), arch_->lut_cells.end(), find_lut) != arch_->lut_cells.end(); + + auto find_pad = [bel_name](t_package_pin p) { return p.bel_name == bel_name; }; + bool is_pad = std::find_if(arch_->pad_bels.begin(), arch_->pad_bels.end(), find_pad) != arch_->pad_bels.end(); + + if (!is_pad) + process_block_ports(leaf_pb_type, cell_name, site, false, is_lut); + + if (is_lut) { + leaf_pb_type->blif_model = nullptr; + process_lut_block(leaf_pb_type); + } else if (is_pad) { + leaf_pb_type->blif_model = nullptr; + process_pad_block(leaf_pb_type, bel, site); + } else { + leaf_pb_type->blif_model = vtr::strdup((std::string(".subckt ") + cell_name).c_str()); + leaf_pb_type->model = get_model(arch_, cell_name); + } + + mode->pb_type_children[count++] = *leaf_pb_type; + } + + process_interconnects(mode, site); + ltypes_.push_back(ltype); + } + } + + void process_lut_block(t_pb_type* lut) { + lut->num_modes = 1; + lut->modes = new t_mode[1]; + + // Check for duplicates + std::string lut_name = lut->name; + auto find_lut = [lut_name](t_lut_bel l) { return l.name == lut_name; }; + auto res = std::find_if(arch_->lut_bels.begin(), arch_->lut_bels.end(), find_lut); + VTR_ASSERT(res != arch_->lut_bels.end()); + auto lut_bel = *res; + + auto mode = &lut->modes[0]; + mode->name = vtr::strdup("lut"); + mode->parent_pb_type = lut; + mode->index = 0; + mode->num_pb_type_children = 1; + mode->pb_type_children = new t_pb_type[1]; + + auto new_leaf = new t_pb_type; + new_leaf->name = vtr::strdup("lut_child"); + new_leaf->num_pb = 1; + new_leaf->parent_mode = mode; + + int num_ports = 2; + new_leaf->num_ports = num_ports; + new_leaf->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + new_leaf->blif_model = vtr::strdup(MODEL_NAMES); + new_leaf->model = get_model(arch_, std::string(MODEL_NAMES)); + + auto in_size = lut_bel.input_pins.size(); + new_leaf->ports[0] = get_generic_port(arch_, new_leaf, IN_PORT, "in", MODEL_NAMES, in_size); + new_leaf->ports[1] = get_generic_port(arch_, new_leaf, OUT_PORT, "out", MODEL_NAMES); + + mode->pb_type_children[0] = *new_leaf; + + // Num inputs + 1 (output pin) + int num_pins = in_size + 1; + + mode->num_interconnect = num_pins; + mode->interconnect = new t_interconnect[num_pins]; + + for (int i = 0; i < num_pins; i++) { + auto ic = new t_interconnect; + + std::stringstream istr; + std::stringstream ostr; + std::string input_string; + std::string output_string; + + if (i < num_pins - 1) { + istr << lut_bel.input_pins[i]; + ostr << "in[" << i << "]"; + input_string = std::string(lut->name) + std::string(".") + istr.str(); + output_string = std::string(new_leaf->name) + std::string(".") + ostr.str(); + } else { + istr << "out"; + ostr << lut_bel.output_pin; + input_string = std::string(new_leaf->name) + std::string(".") + istr.str(); + output_string = std::string(lut->name) + std::string(".") + ostr.str(); + } + std::string name = istr.str() + std::string("_") + ostr.str(); + ic->name = vtr::strdup(name.c_str()); + ic->type = DIRECT_INTERC; + ic->parent_mode_index = 0; + ic->parent_mode = mode; + ic->input_string = vtr::strdup(input_string.c_str()); + ic->output_string = vtr::strdup(output_string.c_str()); + + mode->interconnect[i] = *ic; + } + } + + void process_pad_block(t_pb_type* pad, Device::BEL::Reader& bel, Device::SiteType::Reader& site) { + // For now, hard-code two modes for pads, so that PADs can either be IPADs or OPADs + pad->num_modes = 2; + pad->modes = new t_mode[2]; + + // Add PAD pb_type ports + VTR_ASSERT(bel.getPins().size() == 1); + std::string pin = str(site.getBelPins()[bel.getPins()[0]].getName()); + std::string ipin = pin + "_in"; + std::string opin = pin + "_out"; + + auto num_ports = 2; + auto ports = new t_port[num_ports]; + pad->ports = ports; + pad->num_ports = pad->num_pins = num_ports; + pad->num_input_pins = 1; + pad->num_output_pins = 1; + + int pin_abs = 0; + int pin_count = 0; + for (auto dir : {IN_PORT, OUT_PORT}) { + int pins_dir_count = 0; + t_port* port = &ports[pin_count]; + + port->parent_pb_type = pad; + port->index = pin_count++; + port->port_index_by_type = pins_dir_count++; + port->absolute_first_pin_index = pin_abs++; + + port->equivalent = PortEquivalence::NONE; + port->num_pins = 1; + port->type = dir; + port->is_clock = false; + + bool is_input = dir == IN_PORT; + port->name = is_input ? vtr::strdup(ipin.c_str()) : vtr::strdup(opin.c_str()); + port->model_port = nullptr; + port->port_class = vtr::strdup(nullptr); + port->port_power = (t_port_power*)vtr::calloc(1, sizeof(t_port_power)); + } + + // OPAD mode + auto omode = &pad->modes[0]; + omode->name = vtr::strdup("opad"); + omode->parent_pb_type = pad; + omode->index = 0; + omode->num_pb_type_children = 1; + omode->pb_type_children = new t_pb_type[1]; + + auto opad = new t_pb_type; + opad->name = vtr::strdup("opad"); + opad->num_pb = 1; + opad->parent_mode = omode; + + num_ports = 1; + opad->num_ports = num_ports; + opad->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + opad->blif_model = vtr::strdup(MODEL_OUTPUT); + opad->model = get_model(arch_, std::string(MODEL_OUTPUT)); + + opad->ports[0] = get_generic_port(arch_, opad, IN_PORT, "outpad", MODEL_OUTPUT); + omode->pb_type_children[0] = *opad; + + // IPAD mode + auto imode = &pad->modes[1]; + imode->name = vtr::strdup("ipad"); + imode->parent_pb_type = pad; + imode->index = 1; + imode->num_pb_type_children = 1; + imode->pb_type_children = new t_pb_type[1]; + + auto ipad = new t_pb_type; + ipad->name = vtr::strdup("ipad"); + ipad->num_pb = 1; + ipad->parent_mode = imode; + + num_ports = 1; + ipad->num_ports = num_ports; + ipad->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + ipad->blif_model = vtr::strdup(MODEL_INPUT); + ipad->model = get_model(arch_, std::string(MODEL_INPUT)); + + ipad->ports[0] = get_generic_port(arch_, ipad, OUT_PORT, "inpad", MODEL_INPUT); + imode->pb_type_children[0] = *ipad; + + // Handle interconnects + int num_pins = 1; + + omode->num_interconnect = num_pins; + omode->interconnect = new t_interconnect[num_pins]; + + imode->num_interconnect = num_pins; + imode->interconnect = new t_interconnect[num_pins]; + + std::string opad_istr = std::string(pad->name) + std::string(".") + ipin; + std::string opad_ostr = std::string(opad->name) + std::string(".outpad"); + std::string o_ic_name = std::string(pad->name) + std::string("_") + std::string(opad->name); + + std::string ipad_istr = std::string(ipad->name) + std::string(".inpad"); + std::string ipad_ostr = std::string(pad->name) + std::string(".") + opin; + std::string i_ic_name = std::string(ipad->name) + std::string("_") + std::string(pad->name); + + auto o_ic = new t_interconnect[num_pins]; + auto i_ic = new t_interconnect[num_pins]; + + o_ic->name = vtr::strdup(o_ic_name.c_str()); + o_ic->type = DIRECT_INTERC; + o_ic->parent_mode_index = 0; + o_ic->parent_mode = omode; + o_ic->input_string = vtr::strdup(opad_istr.c_str()); + o_ic->output_string = vtr::strdup(opad_ostr.c_str()); + + i_ic->name = vtr::strdup(i_ic_name.c_str()); + i_ic->type = DIRECT_INTERC; + i_ic->parent_mode_index = 0; + i_ic->parent_mode = imode; + i_ic->input_string = vtr::strdup(ipad_istr.c_str()); + i_ic->output_string = vtr::strdup(ipad_ostr.c_str()); + + omode->interconnect[0] = *o_ic; + imode->interconnect[0] = *i_ic; + } + + void process_block_ports(t_pb_type* pb_type, std::string cell_name, Device::SiteType::Reader& site, bool is_root = true, bool is_model_library = false) { + std::unordered_set names; + + // Prepare data based on pb_type level + std::unordered_map pins; + if (is_root) { + for (auto pin : site.getPins()) { + auto dir = pin.getDir() == LogicalNetlist::Netlist::Direction::INPUT ? IN_PORT : OUT_PORT; + pins.emplace(str(pin.getName()), dir); + } + } else { + for (auto bel : site.getBels()) { + if (bel.getCategory() != Device::BELCategory::LOGIC) + continue; + + if (std::string(pb_type->name) != str(bel.getName())) + continue; + + for (auto bel_pin : bel.getPins()) { + auto pin = site.getBelPins()[bel_pin]; + auto dir = pin.getDir() == LogicalNetlist::Netlist::Direction::INPUT ? IN_PORT : OUT_PORT; + pins.emplace(str(pin.getName()), dir); + } + } + } + + auto num_ports = pins.size(); + auto ports = new t_port[num_ports]; + pb_type->ports = ports; + pb_type->num_ports = pb_type->num_pins = num_ports; + pb_type->num_input_pins = 0; + pb_type->num_output_pins = 0; + + int pin_abs = 0; + int pin_count = 0; + for (auto dir : {IN_PORT, OUT_PORT}) { + int pins_dir_count = 0; + for (auto pin_pair : pins) { + auto pin_name = pin_pair.first; + auto pin_dir = pin_pair.second; + + if (pin_dir != dir) + continue; + + VTR_ASSERT(names.insert(pin_name).second); + + bool is_input = dir == IN_PORT; + pb_type->num_input_pins += is_input ? 1 : 0; + pb_type->num_output_pins += is_input ? 0 : 1; + + auto port = get_generic_port(arch_, pb_type, dir, pin_name); + ports[pin_count] = port; + port.index = pin_count++; + port.port_index_by_type = pins_dir_count++; + port.absolute_first_pin_index = pin_abs++; + + if (!is_root && !is_model_library) + port.model_port = get_model_port(arch_, cell_name, pin_name); + } + } + } + + void process_interconnects(t_mode* mode, Device::SiteType::Reader& site) { + auto ics = get_ics(site); + auto num_ic = ics.size(); + + mode->num_interconnect = num_ic; + mode->interconnect = new t_interconnect[num_ic]; + + int curr_ic = 0; + std::unordered_set names; + + // Handle site wires, namely direct interconnects + for (auto ic_pair : ics) { + std::string ic_name = ic_pair.first; + + std::string inputs; + std::string outputs; + e_interconnect ic_type; + bool add_pack_pattern; + + std::tie(inputs, outputs, ic_type, add_pack_pattern) = ic_pair.second; + + t_interconnect* ic = &mode->interconnect[curr_ic++]; + + if (add_pack_pattern) { + ic->num_annotations = 1; + // pack pattern + auto pp = new t_pin_to_pin_annotation; + + pp->prop = (int*)vtr::calloc(1, sizeof(int)); + pp->value = (char**)vtr::calloc(1, sizeof(char*)); + + pp->type = E_ANNOT_PIN_TO_PIN_PACK_PATTERN; + pp->format = E_ANNOT_PIN_TO_PIN_CONSTANT; + pp->prop[0] = (int)E_ANNOT_PIN_TO_PIN_PACK_PATTERN_NAME; + pp->value[0] = vtr::strdup(ic_name.c_str()); + pp->input_pins = vtr::strdup(inputs.c_str()); + pp->output_pins = vtr::strdup(outputs.c_str()); + pp->num_value_prop_pairs = 1; + pp->clock = nullptr; + ic->annotations = pp; + } + + // No line num for interconnects, as line num is XML specific + // TODO: probably line_num should be deprecated as it is dependent + // on the input architecture format. + ic->line_num = 0; + ic->type = ic_type; + ic->parent_mode_index = mode->index; + ic->parent_mode = mode; + + VTR_ASSERT(names.insert(ic_name).second); + ic->name = vtr::strdup(ic_name.c_str()); + ic->input_string = vtr::strdup(inputs.c_str()); + ic->output_string = vtr::strdup(outputs.c_str()); + } + } + + // Physical Tiles + void process_tiles() { + auto EMPTY = get_empty_physical_type(std::string("NULL")); + int index = 0; + EMPTY.index = index; + ptypes_.push_back(EMPTY); + + auto tileTypeList = ar_.getTileTypeList(); + + for (auto tile : tileTypeList) { + t_physical_tile_type ptype; + auto name = str(tile.getName()); + + if (name == std::string("NULL")) + continue; + + ptype.name = vtr::strdup(name.c_str()); + ptype.index = ++index; + ptype.width = ptype.height = ptype.area = 1; + ptype.capacity = 1; + + process_sub_tiles(ptype, tile); + + setup_pin_classes(&ptype); + + bool is_pad = false; + for (auto site : tile.getSiteTypes()) { + auto site_type = ar_.getSiteTypeList()[site.getPrimaryType()]; + + for (auto bel : site_type.getBels()) { + auto bel_name = str(bel.getName()); + auto is_pad_func = [bel_name](t_package_pin p) { return p.bel_name == bel_name; }; + auto res = std::find_if(arch_->pad_bels.begin(), arch_->pad_bels.end(), is_pad_func); + + is_pad = res != arch_->pad_bels.end() || is_pad; + } + } + + ptype.is_input_type = ptype.is_output_type = is_pad; + + ptypes_.push_back(ptype); + } + } + + void process_sub_tiles(t_physical_tile_type& type, Device::TileType::Reader& tile) { + // TODO: only one subtile at the moment + auto siteTypeList = ar_.getSiteTypeList(); + for (auto site_in_tile : tile.getSiteTypes()) { + t_sub_tile sub_tile; + + auto site = siteTypeList[site_in_tile.getPrimaryType()]; + + sub_tile.index = 0; + sub_tile.name = vtr::strdup(str(site.getName()).c_str()); + sub_tile.capacity.set(0, 0); + + int port_idx = 0; + int abs_first_pin_idx = 0; + int icount = 0; + int ocount = 0; + for (auto dir : {LogicalNetlist::Netlist::Direction::INPUT, LogicalNetlist::Netlist::Direction::OUTPUT}) { + int port_idx_by_type = 0; + for (auto pin : site.getPins()) { + if (pin.getDir() != dir) + continue; + + t_physical_tile_port port; + + port.name = vtr::strdup(str(pin.getName()).c_str()); + port.equivalent = PortEquivalence::NONE; + port.num_pins = 1; + + sub_tile.sub_tile_to_tile_pin_indices.push_back(port_idx); + port.index = port_idx++; + + port.absolute_first_pin_index = abs_first_pin_idx++; + port.port_index_by_type = port_idx_by_type++; + + if (dir == LogicalNetlist::Netlist::Direction::INPUT) { + port.type = IN_PORT; + icount++; + } else { + port.type = OUT_PORT; + ocount++; + } + + sub_tile.ports.push_back(port); + } + } + + auto pins_size = site.getPins().size(); + sub_tile.num_phy_pins += pins_size * type.capacity; + type.num_pins += pins_size * type.capacity; + type.num_inst_pins += pins_size; + + type.num_input_pins += icount; + type.num_output_pins += ocount; + type.num_receivers += icount * type.capacity; + type.num_drivers += ocount * type.capacity; + + type.pin_width_offset.resize(type.num_pins, 0); + type.pin_height_offset.resize(type.num_pins, 0); + + type.pinloc.resize({1, 1, 4}, std::vector(type.num_pins, false)); + for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { + for (int pin = 0; pin < type.num_pins; pin++) { + type.pinloc[0][0][side][pin] = true; + type.pin_width_offset[pin] = 0; + type.pin_height_offset[pin] = 0; + } + } + + auto ltype = get_type_by_name(sub_tile.name, ltypes_); + vtr::bimap directs_map; + + for (int npin = 0; npin < type.num_pins; npin++) { + t_physical_pin physical_pin(npin); + t_logical_pin logical_pin(npin); + + directs_map.insert(logical_pin, physical_pin); + } + + sub_tile.equivalent_sites.push_back(ltype); + + type.tile_block_pin_directs_map[ltype->index][sub_tile.index] = directs_map; + + // Assign FC specs + int iblk_pin = 0; + for (const auto& port : sub_tile.ports) { + t_fc_specification fc_spec; + + fc_spec.seg_index = 0; + + //Apply type and defaults + if (port.type == IN_PORT) { + fc_spec.fc_type = e_fc_type::IN; + fc_spec.fc_value_type = default_fc_.in_value_type; + fc_spec.fc_value = default_fc_.in_value; + } else { + VTR_ASSERT(port.type == OUT_PORT); + fc_spec.fc_type = e_fc_type::OUT; + fc_spec.fc_value_type = default_fc_.out_value_type; + fc_spec.fc_value = default_fc_.out_value; + } + + //Add all the pins from this port + for (int iport_pin = 0; iport_pin < port.num_pins; ++iport_pin) { + int true_physical_blk_pin = sub_tile.sub_tile_to_tile_pin_indices[iblk_pin++]; + fc_spec.pins.push_back(true_physical_blk_pin); + } + + type.fc_specs.push_back(fc_spec); + } + + type.sub_tiles.push_back(sub_tile); + } + } + void process_luts() { // Add LUT Cell definitions // This is helpful to understand which cells are LUTs @@ -271,56 +1085,104 @@ struct ArchReader { arch_->lut_cells.push_back(cell); } + + for (auto lut_elem : lut_def.getLutElements()) { + for (auto lut : lut_elem.getLuts()) { + for (auto bel : lut.getBels()) { + t_lut_bel lut_bel; + + std::string name = bel.getName().cStr(); + lut_bel.name = name; + + // Check for duplicates + auto is_duplicate = [name](t_lut_bel l) { return l.name == name; }; + auto res = std::find_if(arch_->lut_bels.begin(), arch_->lut_bels.end(), is_duplicate); + if (res != arch_->lut_bels.end()) + continue; + + std::vector ipins; + for (auto pin : bel.getInputPins()) + ipins.push_back(pin.cStr()); + + lut_bel.input_pins = ipins; + lut_bel.output_pin = bel.getOutputPin().cStr(); + + arch_->lut_bels.push_back(lut_bel); + } + } + } + } + + void process_package_pins() { + for (auto package : ar_.getPackages()) { + for (auto pin : package.getPackagePins()) { + t_package_pin pckg_pin; + pckg_pin.name = str(pin.getPackagePin()); + + if (pin.getBel().isBel()) + pckg_pin.bel_name = str(pin.getBel().getBel()); + + if (pin.getSite().isSite()) + pckg_pin.site_name = str(pin.getSite().getSite()); + + arch_->pad_bels.push_back(pckg_pin); + } + } } // Layout Processing void process_layout() { - auto strList = ar_.getStrList(); auto tileList = ar_.getTileList(); auto tileTypeList = ar_.getTileTypeList(); - t_grid_def grid_def; - grid_def.width = grid_def.height = 0; - for (auto tile : tileList) { - grid_def.width = std::max(grid_def.width, tile.getCol() + 1); - grid_def.height = std::max(grid_def.height, tile.getRow() + 1); - } + std::vector packages; + for (auto package : ar_.getPackages()) + packages.push_back(str(package.getName())); - grid_def.grid_type = GridDefType::FIXED; - std::string name = std::string(ar_.getName()); + for (auto name : packages) { + t_grid_def grid_def; + grid_def.width = grid_def.height = 0; + for (auto tile : tileList) { + grid_def.width = std::max(grid_def.width, tile.getCol() + 1); + grid_def.height = std::max(grid_def.height, tile.getRow() + 1); + } - if (name == "auto") { - // At the moment, the interchange specifies fixed-layout only architectures, - // and allowing for auto-sizing could potentially be implemented later on - // to allow for experimentation on new architectures. - // For the time being the layout is restricted to be only fixed. - archfpga_throw(arch_file_, __LINE__, - "The name auto is reserved for auto-size layouts; please choose another name"); - } + grid_def.grid_type = GridDefType::FIXED; - grid_def.name = name; - for (auto tile : tileList) { - t_metadata_dict data; - std::string tile_prefix(strList[tile.getName()].cStr()); - auto tileType = tileTypeList[tile.getType()]; - std::string tile_type(strList[tileType.getName()].cStr()); - - size_t pos = tile_prefix.find(tile_type); - if (pos != std::string::npos && pos == 0) - tile_prefix.erase(pos, tile_type.length() + 1); - data.add(arch_->strings.intern_string(vtr::string_view("fasm_prefix")), - arch_->strings.intern_string(vtr::string_view(tile_prefix.c_str()))); - t_grid_loc_def single(tile_type, 1); - single.x.start_expr = tile.getCol(); - single.y.start_expr = tile.getRow(); - single.x.end_expr = single.x.start_expr + " + w - 1"; - single.y.end_expr = single.y.start_expr + " + h - 1"; - single.owned_meta = std::make_unique(data); - single.meta = single.owned_meta.get(); - grid_def.loc_defs.emplace_back(std::move(single)); - } + if (name == "auto") { + // At the moment, the interchange specifies fixed-layout only architectures, + // and allowing for auto-sizing could potentially be implemented later on + // to allow for experimentation on new architectures. + // For the time being the layout is restricted to be only fixed. + archfpga_throw(arch_file_, __LINE__, + "The name auto is reserved for auto-size layouts; please choose another name"); + } + grid_def.name = name; + for (auto tile : tileList) { + t_metadata_dict data; + std::string tile_prefix = str(tile.getName()); + auto tileType = tileTypeList[tile.getType()]; + std::string tile_type = str(tileType.getName()); + + size_t pos = tile_prefix.find(tile_type); + if (pos != std::string::npos && pos == 0) + tile_prefix.erase(pos, tile_type.length() + 1); + data.add(arch_->strings.intern_string(vtr::string_view("fasm_prefix")), + arch_->strings.intern_string(vtr::string_view(tile_prefix.c_str()))); + t_grid_loc_def single(tile_type, 1); + single.x.start_expr = std::to_string(tile.getCol()); + single.y.start_expr = std::to_string(tile.getRow()); + + single.x.end_expr = single.x.start_expr + " + w - 1"; + single.y.end_expr = single.y.start_expr + " + h - 1"; + + single.owned_meta = std::make_unique(data); + single.meta = single.owned_meta.get(); + grid_def.loc_defs.emplace_back(std::move(single)); + } - arch_->grid_layouts.emplace_back(std::move(grid_def)); + arch_->grid_layouts.emplace_back(std::move(grid_def)); + } } void process_device() { @@ -381,15 +1243,14 @@ struct ArchReader { std::string switch_name; arch_->num_switches = num_switches; - auto* switches = arch_->Switches; if (num_switches > 0) { - switches = new t_arch_switch_inf[num_switches]; + arch_->Switches = new t_arch_switch_inf[num_switches]; } float R, Cin, Cint, Cout, Tdel; for (int i = 0; i < (int)num_switches; ++i) { - t_arch_switch_inf& as = switches[i]; + t_arch_switch_inf* as = &arch_->Switches[i]; R = Cin = Cint = Cout = Tdel = 0.0; SwitchType type; @@ -437,32 +1298,30 @@ struct ArchReader { "Switch name '%s' is a reserved name for VPR internal usage!", switch_name.c_str()); } - as.name = vtr::strdup(switch_name.c_str()); - as.set_type(type); - as.mux_trans_size = as.type() == SwitchType::MUX ? 1 : 0; - - as.R = R; - as.Cin = Cin; - as.Cout = Cout; - as.Cinternal = Cint; - as.set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, Tdel); - - if (as.type() == SwitchType::SHORT || as.type() == SwitchType::PASS_GATE) { - as.buf_size_type = BufferSize::ABSOLUTE; - as.buf_size = 0; - as.power_buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; - as.power_buffer_size = 0.; + as->name = vtr::strdup(switch_name.c_str()); + as->set_type(type); + as->mux_trans_size = as->type() == SwitchType::MUX ? 1 : 0; + + as->R = R; + as->Cin = Cin; + as->Cout = Cout; + as->Cinternal = Cint; + as->set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, Tdel); + + if (as->type() == SwitchType::SHORT || as->type() == SwitchType::PASS_GATE) { + as->buf_size_type = BufferSize::ABSOLUTE; + as->buf_size = 0; + as->power_buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; + as->power_buffer_size = 0.; } else { - as.buf_size_type = BufferSize::AUTO; - as.buf_size = 0.; - as.power_buffer_type = POWER_BUFFER_TYPE_AUTO; + as->buf_size_type = BufferSize::AUTO; + as->buf_size = 0.; + as->power_buffer_type = POWER_BUFFER_TYPE_AUTO; } } } void process_segments() { - auto strList = ar_.getStrList(); - // Segment names will be taken from wires connected to pips // They are good representation for nodes std::set wire_names; @@ -479,7 +1338,7 @@ struct ArchReader { for (auto i : wire_names) { // Use default values as we will populate rr_graph with correct values // This segments are just declaration of future use - arch_->Segments[index].name = std::string(strList[i]); + arch_->Segments[index].name = str(i); arch_->Segments[index].length = 1; arch_->Segments[index].frequency = 1; arch_->Segments[index].Rmetal = 0; diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index 563c2be6bbb..ba6b82f28a9 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -109,7 +109,6 @@ struct t_pin_locs { /* Function prototypes */ /* Populate data */ -static void SetupPinClasses(t_physical_tile_type* PhysicalTileType); static void LoadPinLoc(pugi::xml_node Locations, t_physical_tile_type* type, @@ -264,9 +263,6 @@ int find_switch_by_name(const t_arch& arch, std::string switch_name); e_side string_to_side(std::string side_str); -static void link_physical_logical_types(std::vector& PhysicalTileTypes, - std::vector& LogicalBlockTypes); - template static T* get_type_by_name(const char* type_name, std::vector& types); @@ -450,105 +446,6 @@ void XmlReadArch(const char* ArchFile, * */ -/* Sets up the pin classes for the type. */ -static void SetupPinClasses(t_physical_tile_type* PhysicalTileType) { - int i, k; - int pin_count; - int num_class; - - pugi::xml_node Cur; - - for (i = 0; i < PhysicalTileType->num_pins; i++) { - PhysicalTileType->pin_class.push_back(OPEN); - PhysicalTileType->is_ignored_pin.push_back(true); - PhysicalTileType->is_pin_global.push_back(true); - } - - pin_count = 0; - - t_class_range class_range; - - /* Equivalent pins share the same class, non-equivalent pins belong to different pin classes */ - for (auto& sub_tile : PhysicalTileType->sub_tiles) { - int capacity = sub_tile.capacity.total(); - class_range.low = PhysicalTileType->class_inf.size(); - class_range.high = class_range.low - 1; - for (i = 0; i < capacity; ++i) { - for (const auto& port : sub_tile.ports) { - if (port.equivalent != PortEquivalence::NONE) { - t_class class_inf; - num_class = (int)PhysicalTileType->class_inf.size(); - class_inf.num_pins = port.num_pins; - class_inf.equivalence = port.equivalent; - - if (port.type == IN_PORT) { - class_inf.type = RECEIVER; - } else { - VTR_ASSERT(port.type == OUT_PORT); - class_inf.type = DRIVER; - } - - for (k = 0; k < port.num_pins; ++k) { - class_inf.pinlist.push_back(pin_count); - PhysicalTileType->pin_class[pin_count] = num_class; - // clock pins and other specified global ports are initially specified - // as ignored pins (i.e. connections are not created in the rr_graph and - // nets connected to the port are ignored as well). - PhysicalTileType->is_ignored_pin[pin_count] = port.is_clock || port.is_non_clock_global; - // clock pins and other specified global ports are flaged as global - PhysicalTileType->is_pin_global[pin_count] = port.is_clock || port.is_non_clock_global; - - if (port.is_clock) { - PhysicalTileType->clock_pin_indices.push_back(pin_count); - } - - pin_count++; - } - - PhysicalTileType->class_inf.push_back(class_inf); - class_range.high++; - } else if (port.equivalent == PortEquivalence::NONE) { - for (k = 0; k < port.num_pins; ++k) { - t_class class_inf; - num_class = (int)PhysicalTileType->class_inf.size(); - class_inf.num_pins = 1; - class_inf.pinlist.push_back(pin_count); - class_inf.equivalence = port.equivalent; - - if (port.type == IN_PORT) { - class_inf.type = RECEIVER; - } else { - VTR_ASSERT(port.type == OUT_PORT); - class_inf.type = DRIVER; - } - - PhysicalTileType->pin_class[pin_count] = num_class; - // clock pins and other specified global ports are initially specified - // as ignored pins (i.e. connections are not created in the rr_graph and - // nets connected to the port are ignored as well). - PhysicalTileType->is_ignored_pin[pin_count] = port.is_clock || port.is_non_clock_global; - // clock pins and other specified global ports are flaged as global - PhysicalTileType->is_pin_global[pin_count] = port.is_clock || port.is_non_clock_global; - - if (port.is_clock) { - PhysicalTileType->clock_pin_indices.push_back(pin_count); - } - - pin_count++; - - PhysicalTileType->class_inf.push_back(class_inf); - class_range.high++; - } - } - } - } - - PhysicalTileType->sub_tiles[sub_tile.index].class_range = class_range; - } - - VTR_ASSERT(pin_count == PhysicalTileType->num_pins); -} - static void LoadPinLoc(pugi::xml_node Locations, t_physical_tile_type* type, t_pin_locs* pin_locs, @@ -2883,7 +2780,7 @@ static void ProcessTiles(pugi::xml_node Node, /* Alloc the type list. Need one additional t_type_desctiptors: * 1: empty psuedo-type */ - t_physical_tile_type EMPTY_PHYSICAL_TILE_TYPE = SetupEmptyPhysicalType(); + t_physical_tile_type EMPTY_PHYSICAL_TILE_TYPE = get_empty_physical_type(std::string("EMPTY")); EMPTY_PHYSICAL_TILE_TYPE.index = 0; PhysicalTileTypes.push_back(EMPTY_PHYSICAL_TILE_TYPE); @@ -3542,7 +3439,7 @@ static void ProcessSubTiles(pugi::xml_node Node, int num_pins = PhysicalTileType->num_pins; PhysicalTileType->pinloc.resize({width, height, num_sides}, std::vector(num_pins, false)); - SetupPinClasses(PhysicalTileType); + setup_pin_classes(PhysicalTileType); LoadPinLoc(Cur, PhysicalTileType, &pin_locs, loc_data); } @@ -3556,7 +3453,7 @@ static void ProcessComplexBlocks(vtr::string_internment* strings, pugi::xml_node /* Alloc the type list. Need one additional t_type_desctiptors: * 1: empty psuedo-type */ - t_logical_block_type EMPTY_LOGICAL_BLOCK_TYPE = SetupEmptyLogicalType(); + t_logical_block_type EMPTY_LOGICAL_BLOCK_TYPE = get_empty_logical_type(std::string("EMPTY")); EMPTY_LOGICAL_BLOCK_TYPE.index = 0; LogicalBlockTypes.push_back(EMPTY_LOGICAL_BLOCK_TYPE); @@ -4669,111 +4566,6 @@ e_side string_to_side(std::string side_str) { return side; } -static void link_physical_logical_types(std::vector& PhysicalTileTypes, - std::vector& LogicalBlockTypes) { - for (auto& physical_tile : PhysicalTileTypes) { - if (physical_tile.index == EMPTY_TYPE_INDEX) continue; - - auto eq_sites_set = get_equivalent_sites_set(&physical_tile); - auto equivalent_sites = std::vector(eq_sites_set.begin(), eq_sites_set.end()); - - auto criteria = [&physical_tile](const t_logical_block_type* lhs, const t_logical_block_type* rhs) { - int num_pins = physical_tile.num_inst_pins; - - int lhs_num_logical_pins = lhs->pb_type->num_pins; - int rhs_num_logical_pins = rhs->pb_type->num_pins; - - int lhs_diff_num_pins = num_pins - lhs_num_logical_pins; - int rhs_diff_num_pins = num_pins - rhs_num_logical_pins; - - return lhs_diff_num_pins < rhs_diff_num_pins; - }; - - std::sort(equivalent_sites.begin(), equivalent_sites.end(), criteria); - - for (auto& logical_block : LogicalBlockTypes) { - for (auto site : equivalent_sites) { - if (0 == strcmp(logical_block.name, site->pb_type->name)) { - logical_block.equivalent_tiles.push_back(&physical_tile); - break; - } - } - } - } - - for (auto& logical_block : LogicalBlockTypes) { - if (logical_block.index == EMPTY_TYPE_INDEX) continue; - - auto& equivalent_tiles = logical_block.equivalent_tiles; - - if ((int)equivalent_tiles.size() <= 0) { - archfpga_throw(__FILE__, __LINE__, - "Logical Block %s does not have any equivalent tiles.\n", logical_block.name); - } - - std::unordered_map ignored_pins_check_map; - std::unordered_map global_pins_check_map; - - auto criteria = [&logical_block](const t_physical_tile_type* lhs, const t_physical_tile_type* rhs) { - int num_logical_pins = logical_block.pb_type->num_pins; - - int lhs_num_pins = lhs->num_inst_pins; - int rhs_num_pins = rhs->num_inst_pins; - - int lhs_diff_num_pins = lhs_num_pins - num_logical_pins; - int rhs_diff_num_pins = rhs_num_pins - num_logical_pins; - - return lhs_diff_num_pins < rhs_diff_num_pins; - }; - - std::sort(equivalent_tiles.begin(), equivalent_tiles.end(), criteria); - - for (int pin = 0; pin < logical_block.pb_type->num_pins; pin++) { - for (auto& tile : equivalent_tiles) { - auto direct_maps = tile->tile_block_pin_directs_map.at(logical_block.index); - - for (auto& sub_tile : tile->sub_tiles) { - auto equiv_sites = sub_tile.equivalent_sites; - if (std::find(equiv_sites.begin(), equiv_sites.end(), &logical_block) == equiv_sites.end()) { - continue; - } - - auto direct_map = direct_maps.at(sub_tile.index); - - auto result = direct_map.find(t_logical_pin(pin)); - if (result == direct_map.end()) { - archfpga_throw(__FILE__, __LINE__, - "Logical pin %d not present in pin mapping between Tile %s and Block %s.\n", - pin, tile->name, logical_block.name); - } - - int sub_tile_pin_index = result->second.pin; - int phy_index = sub_tile.sub_tile_to_tile_pin_indices[sub_tile_pin_index]; - - bool is_ignored = tile->is_ignored_pin[phy_index]; - bool is_global = tile->is_pin_global[phy_index]; - - auto ignored_result = ignored_pins_check_map.insert(std::pair(pin, is_ignored)); - if (!ignored_result.second && ignored_result.first->second != is_ignored) { - archfpga_throw(__FILE__, __LINE__, - "Physical Tile %s has a different value for the ignored pin (physical pin: %d, logical pin: %d) " - "different from the corresponding pins of the other equivalent site %s\n.", - tile->name, phy_index, pin, logical_block.name); - } - - auto global_result = global_pins_check_map.insert(std::pair(pin, is_global)); - if (!global_result.second && global_result.first->second != is_global) { - archfpga_throw(__FILE__, __LINE__, - "Physical Tile %s has a different value for the global pin (physical pin: %d, logical pin: %d) " - "different from the corresponding pins of the other equivalent sites\n.", - tile->name, phy_index, pin); - } - } - } - } - } -} - template static T* get_type_by_name(const char* type_name, std::vector& types) { for (auto& type : types) { diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 794abdbe6d2..8d3d415340d 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -139,8 +139,9 @@ void SetupVPR(const t_options* Options, int num_inputs = 0; int num_outputs = 0; for (auto& type : device_ctx.physical_tile_types) { - if (strcmp(type.name, EMPTY_BLOCK_NAME) == 0) { + if (type.index == 0) { VTR_ASSERT(device_ctx.EMPTY_PHYSICAL_TILE_TYPE == nullptr); + VTR_ASSERT(type.num_pins == 0); device_ctx.EMPTY_PHYSICAL_TILE_TYPE = &type; } @@ -156,7 +157,9 @@ void SetupVPR(const t_options* Options, device_ctx.EMPTY_LOGICAL_BLOCK_TYPE = nullptr; int max_equivalent_tiles = 0; for (const auto& type : device_ctx.logical_block_types) { - if (0 == strcmp(type.name, EMPTY_BLOCK_NAME)) { + if (type.index == 0) { + VTR_ASSERT(device_ctx.EMPTY_LOGICAL_BLOCK_TYPE == nullptr); + VTR_ASSERT(type.pb_type == nullptr); device_ctx.EMPTY_LOGICAL_BLOCK_TYPE = &type; } diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 944cc9da9c2..322b1b711a0 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -90,8 +90,6 @@ enum class ScreenUpdatePriority { constexpr auto EMPTY_BLOCK_ID = ClusterBlockId(-1); constexpr auto INVALID_BLOCK_ID = ClusterBlockId(-2); -constexpr const char* EMPTY_BLOCK_NAME = "EMPTY"; - /* * Files */ diff --git a/vpr/test/test_interchange_device.cpp b/vpr/test/test_interchange_device.cpp index 49302728d19..53d9a5edacd 100644 --- a/vpr/test/test_interchange_device.cpp +++ b/vpr/test/test_interchange_device.cpp @@ -18,7 +18,7 @@ TEST_CASE("read_interchange_models", "[vpr]") { FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); - std::unordered_set models = {"IB", "OB", "LUT", "DFF", "GND", "VCC"}; + std::unordered_set models = {"IB", "OB", "DFF", "GND", "VCC"}; // Check that there are exactly the expected models for (auto* model = arch.models; model != nullptr; model = model->next) { @@ -67,4 +67,124 @@ TEST_CASE("read_interchange_layout", "[vpr]") { } } +TEST_CASE("read_interchange_luts", "[vpr]") { + t_arch arch; + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + std::unordered_set lut_cell_pins = {"A0", "A1", "A2", "A3"}; + std::unordered_set lut_bel_pins = {"I0", "I1", "I2", "I3"}; + + REQUIRE(arch.lut_cells.size() == 1); + REQUIRE(arch.lut_bels.size() == 1); + + auto lut_cell = arch.lut_cells[0]; + REQUIRE(lut_cell.name == std::string("LUT")); + REQUIRE(lut_cell.init_param == std::string("INIT")); + for (auto lut_pin : lut_cell_pins) + CHECK(std::find(lut_cell.inputs.begin(), lut_cell.inputs.end(), lut_pin) != lut_cell.inputs.end()); + + auto lut_bel = arch.lut_bels[0]; + REQUIRE(lut_bel.name == std::string("LUT")); + REQUIRE(lut_bel.output_pin == std::string("O")); + for (auto lut_pin : lut_bel_pins) + CHECK(std::find(lut_bel.input_pins.begin(), lut_bel.input_pins.end(), lut_pin) != lut_bel.input_pins.end()); +} + +TEST_CASE("read_interchange_pin_packages", "[vpr]") { + t_arch arch; + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + // The device architecture file contains 35 perimetral PADs + REQUIRE(arch.pad_bels.size() == 35); + + int ipad = 0; + for (auto pad_bel : arch.pad_bels) { + REQUIRE(pad_bel.name == std::string("A") + std::to_string(ipad++)); + REQUIRE(pad_bel.bel_name == std::string("PAD")); + } +} + +TEST_CASE("read_interchange_tiles", "[vpr]") { + t_arch arch; + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + std::unordered_set ptypes = {"NULL", "IOB", "PWR", "CLB"}; + + // Check that there are exactly the expected models + for (auto ptype : physical_tile_types) { + std::string name = ptype.name; + REQUIRE(ptypes.find(name) != ptypes.end()); + ptypes.erase(name); + + if (name == std::string("IOB")) { + CHECK(ptype.is_input_type); + CHECK(ptype.is_output_type); + } + } + + REQUIRE(ptypes.size() == 0); +} + +TEST_CASE("read_interchange_pb_types", "[vpr]") { + t_arch arch; + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + std::unordered_set ltypes = {"NULL", "IOPAD", "SLICE", "POWER"}; + + std::unordered_map slice_ports = { + {"L0", PORTS::IN_PORT}, + {"L1", PORTS::IN_PORT}, + {"L2", PORTS::IN_PORT}, + {"L3", PORTS::IN_PORT}, + {"R", PORTS::IN_PORT}, + {"C", PORTS::IN_PORT}, + {"D", PORTS::IN_PORT}, + {"O", PORTS::OUT_PORT}, + {"Q", PORTS::OUT_PORT}}; + + // Check that there are exactly the expected models + for (auto ltype : logical_block_types) { + std::string name = ltype.name; + REQUIRE(ltypes.find(name) != ltypes.end()); + ltypes.erase(name); + + if (ltype.pb_type == nullptr) { + REQUIRE(name == std::string("NULL")); + continue; + } + + bool check_pb_type = name == std::string("SLICE"); + size_t num_visited = 0; + for (auto iport = 0; iport < ltype.pb_type->num_ports; iport++) { + auto port = ltype.pb_type->ports[iport]; + + REQUIRE(port.name != nullptr); + + if (!check_pb_type) + continue; + + auto res = slice_ports.find(std::string(port.name)); + REQUIRE(res != slice_ports.end()); + REQUIRE(res->second == port.type); + num_visited++; + } + + REQUIRE((num_visited == slice_ports.size() || !check_pb_type)); + } + + REQUIRE(ltypes.size() == 0); +} + } // namespace diff --git a/vpr/test/testarch.device b/vpr/test/testarch.device index e9b12b1ac89..da02924ee50 100644 Binary files a/vpr/test/testarch.device and b/vpr/test/testarch.device differ