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..4c6442efff7 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -1738,9 +1738,31 @@ struct t_lut_cell { std::vector inputs; }; +struct t_lut_bel { + std::string name; + + std::vector input_pins; + std::string output_pin; + + bool operator==(const t_lut_bel& other) const { + return name == other.name && input_pins == other.input_pins && output_pin == other.output_pin; + } +}; + +struct t_lut_element { + std::string site_type; + int width; + std::vector lut_bels; + + bool operator==(const t_lut_element& other) const { + return site_type == other.site_type && width == other.width && lut_bels == other.lut_bels; + } +}; + /* Detailed routing architecture */ struct t_arch { mutable vtr::string_internment strings; + std::vector interned_strings; char* architecture_id; //Secure hash digest of the architecture file to uniquely identify this architecture @@ -1787,6 +1809,7 @@ struct t_arch { // Luts std::vector lut_cells; + std::unordered_map> lut_elements; //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..b32455c6af6 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,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 +36,48 @@ using namespace DeviceResources; using namespace LogicalNetlist; using namespace capnp; +// Necessary to reduce code verbosity when getting the pin directions +static const auto INPUT = LogicalNetlist::Netlist::Direction::INPUT; +static const auto OUTPUT = LogicalNetlist::Netlist::Direction::OUTPUT; +static const auto INOUT = LogicalNetlist::Netlist::Direction::INOUT; + +static const auto LOGIC = Device::BELCategory::LOGIC; +static const auto ROUTING = Device::BELCategory::ROUTING; +static const auto SITE_PORT = Device::BELCategory::SITE_PORT; + +// Enum for pack pattern expansion direction +enum e_pp_dir { + FORWARD = 0, + BACKWARD = 1 +}; + +struct t_package_pin { + std::string name; + + std::string site_name; + std::string bel_name; +}; + +struct t_bel_cell_mapping { + size_t cell; + size_t site; + std::vector> pins; + + bool operator<(const t_bel_cell_mapping& other) const { + return cell < other.cell || (cell == other.cell && site < other.site); + } +}; + +// Intermediate data type to store information on interconnects to be created +struct t_ic_data { + std::string input; + std::set outputs; + + bool requires_pack_pattern; +}; + +/****************** 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,25 +154,154 @@ static float get_corner_value(Device::CornerModel::Reader model, const char* spe return 0.; } +/** @brief Returns the port corresponding to the given model in the architecture */ +static t_model_ports* get_model_port(t_arch* arch, std::string model, std::string port, bool fail = true) { + 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; + } + } + + if (fail) + archfpga_throw(__FILE__, __LINE__, + "Could not find model port: %s (%s)\n", port.c_str(), model.c_str()); + + return nullptr; +} + +/** @brief Returns the specified architecture model */ +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()); +} + +/** @brief Returns the physical or logical type by its name */ +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); +} + +/** @brief Returns a generic port instantiation for a complex block */ +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.is_non_clock_global = 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; +} + +/** @brief Returns true if a given port name exists in the given complex block */ +static bool block_port_exists(t_pb_type* pb_type, std::string port_name) { + for (int iport = 0; iport < pb_type->num_ports; iport++) { + const t_port port = pb_type->ports[iport]; + + if (std::string(port.name) == port_name) + return true; + } + + return false; +} + +/** @brief Returns a pack pattern given it's name, input and output strings */ +static t_pin_to_pin_annotation get_pack_pattern(std::string pp_name, std::string input, std::string output) { + t_pin_to_pin_annotation pp; + + 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(pp_name.c_str()); + pp.input_pins = vtr::strdup(input.c_str()); + pp.output_pins = vtr::strdup(output.c_str()); + pp.num_value_prop_pairs = 1; + pp.clock = nullptr; + + return pp; +} + +/****************** 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 (std::string str : ar_.getStrList()) { + auto interned_string = arch_->strings.intern_string(vtr::string_view(str.c_str())); + arch_->interned_strings.push_back(interned_string); + } } void read_arch() { + // Preprocess arch information process_luts(); + process_package_pins(); + process_cell_bel_mappings(); + process_constants(); + process_bels_and_sites(); + process_models(); process_device(); process_layout(); process_switches(); process_segments(); + + process_sites(); + process_tiles(); + + link_physical_logical_types(ptypes_, ltypes_); + + SyncModelsPbTypes(arch_, ltypes_); + check_models(arch_); } private: @@ -140,6 +313,516 @@ struct ArchReader { t_default_fc_spec default_fc_; + std::string bel_dedup_suffix_ = "_bel"; + + std::unordered_set take_bels_; + std::unordered_set take_sites_; + + // Package pins + + // TODO: add possibility to have multiple packages + std::vector package_pins_; + std::unordered_set pad_bels_; + std::string out_suffix_ = "_out"; + std::string in_suffix_ = "_in"; + + // Bel Cell mappings + std::unordered_map> bel_cell_mappings_; + std::unordered_map segment_name_to_segment_idx; + + // Utils + + /** @brief Returns the string corresponding to the given index */ + std::string str(size_t idx) { + return arch_->interned_strings[idx].get(&arch_->strings); + } + + /** @brief Get the BEL count of a site depending on its category (e.g. logic or routing BELs) */ + int get_bel_type_count(Device::SiteType::Reader& site, Device::BELCategory category, bool skip_lut = false) { + int count = 0; + for (auto bel : site.getBels()) { + auto bel_name = str(bel.getName()); + bool is_logic = category == LOGIC; + + if (skip_lut && is_lut(bel_name, str(site.getName()))) + continue; + + bool skip_bel = is_logic && take_bels_.count(bel.getName()) == 0; + + if (bel.getCategory() == category && !skip_bel) + count++; + } + + return count; + } + + /** @brief Get the BEL reader given its name and site */ + 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); + } + + /** @brief Get the BEL pin reader given its name, site and corresponding BEL */ + Device::BELPin::Reader get_bel_pin_reader(Device::SiteType::Reader& site, Device::BEL::Reader& bel, std::string pin_name) { + auto bel_pins = site.getBelPins(); + + for (auto bel_pin : bel.getPins()) { + auto pin_reader = bel_pins[bel_pin]; + if (str(pin_reader.getName()) == pin_name) + return pin_reader; + } + VTR_ASSERT(0); + } + + /** @brief Get the BEL name, with an optional deduplication suffix in case its name collides with the site name */ + std::string get_bel_name(Device::SiteType::Reader& site, Device::BEL::Reader& bel) { + if (bel.getCategory() == SITE_PORT) + return str(site.getName()); + + auto site_name = str(site.getName()); + auto bel_name = str(bel.getName()); + + return site_name == bel_name ? bel_name + bel_dedup_suffix_ : bel_name; + } + + /** @brief Returns the name of the input argument BEL with optionally the de-duplication suffix removed */ + std::string remove_bel_suffix(std::string bel) { + std::smatch regex_matches; + std::string regex = std::string("(.*)") + bel_dedup_suffix_; + const std::regex bel_regex(regex.c_str()); + if (std::regex_match(bel, regex_matches, bel_regex)) + return regex_matches[1].str(); + + return bel; + } + + /** @brief Returns true in case the input argument corresponds to the name of a LUT */ + bool is_lut(std::string name, const std::string site = std::string()) { + for (auto cell : arch_->lut_cells) + if (cell.name == name) + return true; + + for (const auto& it : arch_->lut_elements) { + if (!site.empty() && site != it.first) { + continue; + } + + for (const auto& lut_element : it.second) { + for (const auto& lut_bel : lut_element.lut_bels) { + if (lut_bel.name == name) { + return true; + } + } + } + } + + return false; + } + + t_lut_element* get_lut_element_for_bel(const std::string& site_type, const std::string& bel_name) { + if (!arch_->lut_elements.count(site_type)) { + return nullptr; + } + + for (auto& lut_element : arch_->lut_elements.at(site_type)) { + for (auto& lut_bel : lut_element.lut_bels) { + if (lut_bel.name == bel_name) { + return &lut_element; + } + } + } + + return nullptr; + } + + /** @brief Returns true in case the input argument corresponds to a PAD BEL */ + bool is_pad(std::string name) { + return pad_bels_.count(name) != 0; + } + + /** @brief Returns an intermediate map representing all the interconnects to be added in a site */ + std::unordered_map get_interconnects(Device::SiteType::Reader& site) { + // dictionary: + // - key: interconnect name + // - value: interconnect data + std::unordered_map ics; + + const std::string site_type = str(site.getName()); + + for (auto wire : site.getSiteWires()) { + std::string wire_name = str(wire.getName()); + + // pin name, bel name + int pin_id = OPEN; + bool pad_exists = false; + bool all_inout_pins = true; + std::string pad_bel_name; + std::string pad_bel_pin_name; + for (auto pin : wire.getPins()) { + auto bel_pin = site.getBelPins()[pin]; + auto dir = bel_pin.getDir(); + std::string bel_pin_name = str(bel_pin.getName()); + + auto bel = get_bel_reader(site, str(bel_pin.getBel())); + auto bel_name = get_bel_name(site, bel); + + auto bel_is_pad = is_pad(bel_name); + + pad_exists |= bel_is_pad; + all_inout_pins &= dir == INOUT; + + if (bel_is_pad) { + pad_bel_name = bel_name; + pad_bel_pin_name = bel_pin_name; + } + + if (dir == OUTPUT) + pin_id = pin; + } + + if (pin_id == OPEN) { + // If no driver pin has been found, the assumption is that + // there must be a PAD with inout pin connected to other inout pins + for (auto pin : wire.getPins()) { + auto bel_pin = site.getBelPins()[pin]; + std::string bel_pin_name = str(bel_pin.getName()); + + auto bel = get_bel_reader(site, str(bel_pin.getBel())); + auto bel_name = get_bel_name(site, bel); + + if (!is_pad(bel_name)) + continue; + + pin_id = pin; + } + } + + VTR_ASSERT(pin_id != OPEN); + + auto out_pin = site.getBelPins()[pin_id]; + auto out_pin_bel = get_bel_reader(site, str(out_pin.getBel())); + auto out_pin_name = str(out_pin.getName()); + + for (auto pin : wire.getPins()) { + if ((int)pin == pin_id) + continue; + + auto bel_pin = site.getBelPins()[pin]; + std::string out_bel_pin_name = str(bel_pin.getName()); + + auto out_bel = get_bel_reader(site, str(bel_pin.getBel())); + auto out_bel_name = get_bel_name(site, out_bel); + + auto in_bel = out_pin_bel; + auto in_bel_name = get_bel_name(site, in_bel); + auto in_bel_pin_name = out_pin_name; + + bool skip_in_bel = in_bel.getCategory() == LOGIC && take_bels_.count(in_bel.getName()) == 0; + bool skip_out_bel = out_bel.getCategory() == LOGIC && take_bels_.count(out_bel.getName()) == 0; + if (skip_in_bel || skip_out_bel) + continue; + + // LUT bels are nested under pb_types which represent LUT + // elements. Check if a BEL belongs to a LUT element and + // adjust pb_type name in the interconnect accordingly. + auto get_lut_element_index = [&](const std::string& bel_name) { + auto lut_element = get_lut_element_for_bel(site_type, bel_name); + if (lut_element == nullptr) + return -1; + + const auto& lut_elements = arch_->lut_elements.at(site_type); + auto it = std::find(lut_elements.begin(), lut_elements.end(), *lut_element); + VTR_ASSERT(it != lut_elements.end()); + + return (int)std::distance(lut_elements.begin(), it); + }; + + // TODO: This avoids having LUTs that can be used in other ways than LUTs, e.g. as DRAMs. + // Once support is added for macro expansion, all the connections currently marked as + // invalid will be re-enabled. + auto is_lut_connection_valid = [&](const std::string& bel_name, const std::string& pin_name) { + auto lut_element = get_lut_element_for_bel(site_type, bel_name); + if (lut_element == nullptr) + return false; + + bool pin_found = false; + for (auto lut_bel : lut_element->lut_bels) { + for (auto lut_bel_pin : lut_bel.input_pins) + pin_found |= lut_bel_pin == pin_name; + + pin_found |= lut_bel.output_pin == pin_name; + } + + if (!pin_found) + return false; + + return true; + }; + + int index = get_lut_element_index(out_bel_name); + bool valid_lut = is_lut_connection_valid(out_bel_name, out_bel_pin_name); + if (index >= 0) { + out_bel_name = "LUT" + std::to_string(index); + + if (!valid_lut) + continue; + } + + index = get_lut_element_index(in_bel_name); + valid_lut = is_lut_connection_valid(in_bel_name, in_bel_pin_name); + if (index >= 0) { + in_bel_name = "LUT" + std::to_string(index); + + if (!valid_lut) + continue; + } + + std::string ostr = out_bel_name + "." + out_bel_pin_name; + std::string istr = in_bel_name + "." + in_bel_pin_name; + + // TODO: If the bel pin is INOUT (e.g. PULLDOWN/PULLUP in Series7) + // for now treat as input only and assign the in suffix + if (bel_pin.getDir() == INOUT && !all_inout_pins && !is_pad(out_bel_name)) + ostr += in_suffix_; + + auto ic_name = wire_name + "_" + out_bel_pin_name; + + bool requires_pack_pattern = pad_exists; + + std::vector> ics_data; + if (all_inout_pins) { + std::string extra_istr = out_bel_name + "." + out_bel_pin_name + out_suffix_; + std::string extra_ostr = in_bel_name + "." + in_bel_pin_name + in_suffix_; + std::string extra_ic_name = ic_name + "_extra"; + + std::set extra_ostrs{extra_ostr}; + t_ic_data extra_ic_data = { + extra_istr, // ic input + extra_ostrs, // ic outputs + requires_pack_pattern // pack pattern required + }; + + ics_data.push_back(std::make_pair(extra_ic_name, extra_ic_data)); + + istr += out_suffix_; + ostr += in_suffix_; + } else if (pad_exists) { + if (out_bel_name == pad_bel_name) + ostr += in_suffix_; + else { // Create new wire to connect PAD output to the BELs input + ic_name = wire_name + "_" + pad_bel_pin_name + out_suffix_; + istr = pad_bel_name + "." + pad_bel_pin_name + out_suffix_; + } + } + + std::set ostrs{ostr}; + t_ic_data ic_data = { + istr, + ostrs, + requires_pack_pattern}; + + ics_data.push_back(std::make_pair(ic_name, ic_data)); + + for (auto entry : ics_data) { + auto name = entry.first; + auto data = entry.second; + + auto res = ics.emplace(name, data); + + if (!res.second) { + auto old_data = res.first->second; + + VTR_ASSERT(old_data.input == data.input); + VTR_ASSERT(data.outputs.size() == 1); + + for (auto out : data.outputs) + res.first->second.outputs.insert(out); + res.first->second.requires_pack_pattern |= data.requires_pack_pattern; + } + } + } + } + + return ics; + } + + /** + * Preprocessors: + * - process_bels_and_sites: information on whether sites and bels need to be expanded in pb types + * - process_luts: processes information on which cells and bels are LUTs + * - process_package_pins: processes information on the device's pinout and which sites and bels + * contain IO pads + * - process_cell_bel_mapping: processes mappings between a cell and the possible BELs location for that cell + * - process_constants: processes constants cell and net names + */ + void process_bels_and_sites() { + auto tiles = ar_.getTileList(); + auto tile_types = ar_.getTileTypeList(); + auto site_types = ar_.getSiteTypeList(); + + for (auto tile : tiles) { + auto tile_type = tile_types[tile.getType()]; + + for (auto site : tile.getSites()) { + auto site_type_in_tile = tile_type.getSiteTypes()[site.getType()]; + auto site_type = site_types[site_type_in_tile.getPrimaryType()]; + + bool found = false; + for (auto bel : site_type.getBels()) { + auto bel_name = bel.getName(); + bool res = bel_cell_mappings_.find(bel_name) != bel_cell_mappings_.end(); + + found |= res; + + if (res || is_pad(str(bel_name))) + take_bels_.insert(bel_name); + } + + if (found) + take_sites_.insert(site_type.getName()); + + // TODO: Enable also alternative site types handling + } + } + } + + void process_luts() { + // Add LUT Cell definitions + // This is helpful to understand which cells are LUTs + auto lut_def = ar_.getLutDefinitions(); + + for (auto lut_cell : lut_def.getLutCells()) { + t_lut_cell cell; + cell.name = lut_cell.getCell().cStr(); + for (auto input : lut_cell.getInputPins()) + cell.inputs.push_back(input.cStr()); + + auto equation = lut_cell.getEquation(); + if (equation.isInitParam()) + cell.init_param = equation.getInitParam().cStr(); + + arch_->lut_cells.push_back(cell); + } + + for (auto lut_elem : lut_def.getLutElements()) { + for (auto lut : lut_elem.getLuts()) { + t_lut_element element; + element.site_type = lut_elem.getSite().cStr(); + element.width = lut.getWidth(); + + for (auto bel : lut.getBels()) { + t_lut_bel lut_bel; + lut_bel.name = bel.getName().cStr(); + 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(); + + element.lut_bels.push_back(lut_bel); + } + + arch_->lut_elements[element.site_type].push_back(element); + } + } + } + + 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()); + pad_bels_.insert(pckg_pin.bel_name); + } + + if (pin.getSite().isSite()) + pckg_pin.site_name = str(pin.getSite().getSite()); + + package_pins_.push_back(pckg_pin); + } + } + } + + void process_cell_bel_mappings() { + auto primLib = ar_.getPrimLibs(); + auto portList = primLib.getPortList(); + + for (auto cell_mapping : ar_.getCellBelMap()) { + size_t cell_name = cell_mapping.getCell(); + + int found_valid_prim = false; + for (auto primitive : primLib.getCellDecls()) { + bool is_prim = str(primitive.getLib()) == std::string("primitives"); + bool is_cell = cell_name == primitive.getName(); + + bool has_inout = false; + for (auto port_idx : primitive.getPorts()) { + auto port = portList[port_idx]; + + if (port.getDir() == INOUT) { + has_inout = true; + break; + } + } + + if (is_prim && is_cell && !has_inout) { + found_valid_prim = true; + break; + } + } + + if (!found_valid_prim) + continue; + + for (auto common_pins : cell_mapping.getCommonPins()) { + std::vector> pins; + + for (auto pin_map : common_pins.getPins()) + pins.emplace_back(pin_map.getCellPin(), pin_map.getBelPin()); + + for (auto site_type_entry : common_pins.getSiteTypes()) { + size_t site_type = site_type_entry.getSiteType(); + + for (auto bel : site_type_entry.getBels()) { + t_bel_cell_mapping mapping; + + mapping.cell = cell_name; + mapping.site = site_type; + mapping.pins = pins; + + std::set maps{mapping}; + auto res = bel_cell_mappings_.emplace(bel, maps); + if (!res.second) { + res.first->second.insert(mapping); + } + } + } + } + } + } + + void process_constants() { + auto consts = ar_.getConstants(); + + arch_->gnd_cell = str(consts.getGndCellType()); + arch_->vcc_cell = str(consts.getVccCellType()); + + if (consts.getGndNetName().isName()) + arch_->gnd_net = str(consts.getGndNetName().getName()); + + if (consts.getVccNetName().isName()) + arch_->vcc_net = str(consts.getVccNetName().getName()); + } + + /* end preprocessors */ + // Model processing void process_models() { // Populate the common library, namely .inputs, .outputs, .names, .latches @@ -152,23 +835,49 @@ 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()); + + if (is_lut(prim_name)) + continue; + + // Check whether the model can be placed in at least one + // BEL that was marked as valid (e.g. added to the take_bels_ data structure) + bool has_bel = false; + for (auto bel_cell_map : bel_cell_mappings_) { + auto bel_name = bel_cell_map.first; + + bool take_bel = take_bels_.count(bel_name) != 0; + + if (!take_bel || is_lut(str(bel_name))) + continue; + + for (auto map : bel_cell_map.second) + has_bel |= primitive.getName() == map.cell; + } + + if (!has_bel) + 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(prim_name.c_str()); + ret_map_name = model_name_map.insert(std::pair(temp->name, 0)); if (!ret_map_name.second) { archfpga_throw(arch_file_, __LINE__, "Duplicate model name: '%s'.\n", temp->name); } - process_model_ports(temp, primitive); + if (!process_model_ports(temp, primitive)) { + free_arch_model(temp); + continue; + } check_model_clocks(temp, arch_file_, __LINE__); check_model_combinational_sinks(temp, arch_file_, __LINE__); @@ -182,11 +891,9 @@ struct ArchReader { arch_->models = temp; } } - return; } - void process_model_ports(t_model* model, Netlist::CellDeclaration::Reader primitive) { - auto strList = ar_.getStrList(); + bool process_model_ports(t_model* model, Netlist::CellDeclaration::Reader primitive) { auto primLib = ar_.getPrimLibs(); auto portList = primLib.getPortList(); @@ -196,21 +903,21 @@ struct ArchReader { auto port = portList[port_idx]; enum PORTS dir = ERR_PORT; switch (port.getDir()) { - case LogicalNetlist::Netlist::Direction::INPUT: + case INPUT: dir = IN_PORT; break; - case LogicalNetlist::Netlist::Direction::OUTPUT: + case OUTPUT: dir = OUT_PORT; break; - case LogicalNetlist::Netlist::Direction::INOUT: - dir = INOUT_PORT; + case INOUT: + return false; break; default: break; } 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 @@ -252,75 +959,1135 @@ struct ArchReader { model->outputs = model_port; } } + + return true; } - void process_luts() { - // Add LUT Cell definitions - // This is helpful to understand which cells are LUTs - auto lut_def = ar_.getLutDefinitions(); + // Complex Blocks + void process_sites() { + auto siteTypeList = ar_.getSiteTypeList(); - for (auto lut_cell : lut_def.getLutCells()) { - t_lut_cell cell; - cell.name = lut_cell.getCell().cStr(); - for (auto input : lut_cell.getInputPins()) - cell.inputs.push_back(input.cStr()); + int index = 0; + // TODO: Make this dynamic depending on data from the interchange + auto EMPTY = get_empty_logical_type(std::string("NULL")); + EMPTY.index = index; + ltypes_.push_back(EMPTY); - auto equation = lut_cell.getEquation(); - if (equation.isInitParam()) - cell.init_param = equation.getInitParam().cStr(); + for (auto site : siteTypeList) { + auto bels = site.getBels(); - arch_->lut_cells.push_back(cell); + if (bels.size() == 0) + continue; + + t_logical_block_type ltype; + + std::string name = str(site.getName()); + + if (take_sites_.count(site.getName()) == 0) + continue; + + // 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, 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 mode = &pb_type->modes[0]; + mode->parent_pb_type = pb_type; + mode->index = 0; + mode->name = vtr::strdup("default"); + mode->disable_packing = false; + + // Get LUT elements for this site + std::vector lut_elements; + if (arch_->lut_elements.count(name)) + lut_elements = arch_->lut_elements.at(name); + + // Count non-LUT BELs plus LUT elements + int block_count = get_bel_type_count(site, LOGIC, true) + get_bel_type_count(site, ROUTING, true) + lut_elements.size(); + + mode->num_pb_type_children = block_count; + mode->pb_type_children = new t_pb_type[mode->num_pb_type_children]; + + // Add regular BELs + int count = 0; + for (auto bel : bels) { + auto category = bel.getCategory(); + if (bel.getCategory() == SITE_PORT) + continue; + + bool is_logic = category == LOGIC; + + if (take_bels_.count(bel.getName()) == 0 && is_logic) + continue; + + if (is_lut(str(bel.getName()), name)) + continue; + + auto bel_name = str(bel.getName()); + std::pair key(name, bel_name); + + auto mid_pb_type = &mode->pb_type_children[count++]; + std::string mid_pb_type_name = bel_name == name ? bel_name + bel_dedup_suffix_ : bel_name; + + mid_pb_type->name = vtr::strdup(mid_pb_type_name.c_str()); + mid_pb_type->num_pb = 1; + mid_pb_type->parent_mode = mode; + mid_pb_type->blif_model = nullptr; + + if (!is_pad(bel_name)) + process_block_ports(mid_pb_type, site, bel.getName()); + + if (is_pad(bel_name)) + process_pad_block(mid_pb_type, bel, site); + else if (is_logic) + process_generic_block(mid_pb_type, bel, site); + else { + VTR_ASSERT(category == ROUTING); + process_routing_block(mid_pb_type); + } + } + + // Add LUT elements + for (size_t i = 0; i < lut_elements.size(); ++i) { + const auto& lut_element = lut_elements[i]; + + auto mid_pb_type = &mode->pb_type_children[count++]; + mid_pb_type->name = vtr::stringf("LUT%d", i); + mid_pb_type->num_pb = 1; + mid_pb_type->parent_mode = mode; + mid_pb_type->blif_model = nullptr; + + process_lut_element(mid_pb_type, lut_element); + } + + process_interconnects(mode, site); + ltypes_.push_back(ltype); } } - // Layout Processing - void process_layout() { - auto strList = ar_.getStrList(); - auto tileList = ar_.getTileList(); - auto tileTypeList = ar_.getTileTypeList(); - t_grid_def grid_def; + /** @brief Processes a LUT element starting from the intermediate pb type */ + void process_lut_element(t_pb_type* parent, const t_lut_element& lut_element) { + // Collect ports for the parent pb_type representing the whole LUT + // element + std::set> parent_ports; + for (const auto& lut_bel : lut_element.lut_bels) { + for (const auto& name : lut_bel.input_pins) { + parent_ports.emplace(name, IN_PORT, 1); + } - 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); + parent_ports.emplace(lut_bel.output_pin, OUT_PORT, 1); } - grid_def.grid_type = GridDefType::FIXED; - std::string name = std::string(ar_.getName()); + // Create the ports + create_ports(parent, parent_ports); + + // Make a single mode for each member LUT of the LUT element + parent->num_modes = (int)lut_element.lut_bels.size(); + parent->modes = new t_mode[parent->num_modes]; + + for (size_t i = 0; i < lut_element.lut_bels.size(); ++i) { + const t_lut_bel& lut_bel = lut_element.lut_bels[i]; + auto mode = &parent->modes[i]; + + mode->name = vtr::strdup(lut_bel.name.c_str()); + mode->parent_pb_type = parent; + mode->index = i; + + // Leaf pb_type block for the LUT + mode->num_pb_type_children = 1; + mode->pb_type_children = new t_pb_type[mode->num_pb_type_children]; + + auto pb_type = &mode->pb_type_children[0]; + pb_type->name = vtr::strdup(lut_bel.name.c_str()); + pb_type->num_pb = 1; + pb_type->parent_mode = mode; + pb_type->blif_model = nullptr; + + process_lut_block(pb_type, lut_bel); - 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"); + // Mode interconnect + mode->num_interconnect = lut_bel.input_pins.size() + 1; + mode->interconnect = new t_interconnect[mode->num_interconnect]; + + // Inputs + for (size_t j = 0; j < lut_bel.input_pins.size(); ++j) { + auto* ic = &mode->interconnect[j]; + + ic->type = DIRECT_INTERC; + ic->parent_mode = mode; + ic->parent_mode_index = mode->index; + + ic->input_string = vtr::stringf("%s.%s", parent->name, lut_bel.input_pins[j].c_str()); + ic->output_string = vtr::stringf("%s.in[%d]", pb_type->name, j); + ic->name = vtr::stringf("%s_to_%s", + ic->input_string, + ic->output_string); + } + + // Output + auto* ic = &mode->interconnect[mode->num_interconnect - 1]; + ic->type = DIRECT_INTERC; + ic->parent_mode = mode; + ic->parent_mode_index = mode->index; + + ic->input_string = vtr::stringf("%s.out", pb_type->name); + ic->output_string = vtr::stringf("%s.%s", parent->name, lut_bel.output_pin.c_str()); + ic->name = vtr::stringf("%s_to_%s", + ic->input_string, + ic->output_string); } + } - 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()); + /** @brief Processes a LUT primitive starting from the intermediate pb type */ + void process_lut_block(t_pb_type* pb_type, const t_lut_bel& lut_bel) { + // Create port list + size_t width = lut_bel.input_pins.size(); + + std::set> ports; + ports.emplace("in", IN_PORT, width); + ports.emplace("out", OUT_PORT, 1); + + create_ports(pb_type, ports); + + // Make two modes. One for LUT-thru and another for the actual LUT bel + pb_type->num_modes = 2; + pb_type->modes = new t_mode[pb_type->num_modes]; + + // ................................................ + // LUT-thru + t_mode* mode = &pb_type->modes[0]; + + // Mode + mode->name = vtr::strdup("wire"); + mode->parent_pb_type = pb_type; + mode->index = 0; + mode->num_pb_type_children = 0; + + // Mode interconnect + mode->num_interconnect = 1; + mode->interconnect = new t_interconnect[mode->num_interconnect]; + t_interconnect* ic = &mode->interconnect[0]; + + ic->input_string = vtr::stringf("%s.in", pb_type->name); + ic->output_string = vtr::stringf("%s.out", pb_type->name); + ic->name = vtr::strdup("passthrough"); + + ic->type = COMPLETE_INTERC; + ic->parent_mode = mode; + ic->parent_mode_index = mode->index; + + // ................................................ + // LUT BEL + mode = &pb_type->modes[1]; + + // Mode + mode->name = vtr::strdup("lut"); + mode->parent_pb_type = pb_type; + mode->index = 1; + + // Leaf pb_type + mode->num_pb_type_children = 1; + mode->pb_type_children = new t_pb_type[mode->num_pb_type_children]; + + auto lut = &mode->pb_type_children[0]; + lut->name = vtr::strdup("lut"); + lut->num_pb = 1; + lut->parent_mode = mode; + + lut->blif_model = vtr::strdup(MODEL_NAMES); + lut->model = get_model(arch_, std::string(MODEL_NAMES)); + + lut->num_ports = 2; + lut->ports = (t_port*)vtr::calloc(lut->num_ports, sizeof(t_port)); + lut->ports[0] = get_generic_port(arch_, lut, IN_PORT, "in", MODEL_NAMES, width); + lut->ports[1] = get_generic_port(arch_, lut, OUT_PORT, "out", MODEL_NAMES); + + lut->ports[0].equivalent = PortEquivalence::FULL; + + // Set classes + pb_type->class_type = LUT_CLASS; + lut->class_type = LUT_CLASS; + lut->ports[0].port_class = vtr::strdup("lut_in"); + lut->ports[1].port_class = vtr::strdup("lut_out"); + + // Mode interconnect + mode->num_interconnect = 2; + mode->interconnect = new t_interconnect[mode->num_interconnect]; + + // Input + ic = &mode->interconnect[0]; + ic->type = DIRECT_INTERC; + ic->parent_mode = mode; + ic->parent_mode_index = mode->index; + + ic->input_string = vtr::stringf("%s.in", pb_type->name); + ic->output_string = vtr::stringf("%s.in", lut->name); + ic->name = vtr::stringf("%s_to_%s", + ic->input_string, + ic->output_string); + + // Output + ic = &mode->interconnect[1]; + ic->type = DIRECT_INTERC; + ic->parent_mode = mode; + ic->parent_mode_index = mode->index; + + ic->input_string = vtr::stringf("%s.out", lut->name); + ic->output_string = vtr::stringf("%s.out", pb_type->name); + ic->name = vtr::stringf("%s_to_%s", + ic->input_string, + ic->output_string); + } - 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)); + /** @brief Generates the leaf pb types for the PAD type */ + 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_suffix_; + std::string opin = pin + out_suffix_; + + 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)); } - arch_->grid_layouts.emplace_back(std::move(grid_def)); + // 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; + } + + /** @brief Generates the leaf pb types for a generic intermediate block, with as many modes + * as the number of models that can be used in this complex block. + */ + void process_generic_block(t_pb_type* pb_type, Device::BEL::Reader& bel, Device::SiteType::Reader& site) { + std::string pb_name = std::string(pb_type->name); + + std::set maps(bel_cell_mappings_[bel.getName()]); + + std::vector map_to_erase; + for (auto map : maps) { + auto name = str(map.cell); + bool is_compatible = map.site == site.getName(); + + for (auto pin_map : map.pins) { + if (is_compatible == false) + break; + + auto cell_pin = str(pin_map.first); + auto bel_pin = str(pin_map.second); + + if (cell_pin == arch_->vcc_cell || cell_pin == arch_->gnd_cell) + continue; + + // Assign suffix to bel pin as it is a inout pin which was split in out and in ports + auto pin_reader = get_bel_pin_reader(site, bel, bel_pin); + bool is_inout = pin_reader.getDir() == INOUT; + + auto model_port = get_model_port(arch_, name, cell_pin, false); + + if (is_inout && model_port != nullptr) + bel_pin = model_port->dir == IN_PORT ? bel_pin + in_suffix_ : bel_pin + out_suffix_; + + is_compatible &= block_port_exists(pb_type, bel_pin); + } + + if (!is_compatible) + map_to_erase.push_back(map); + } + + for (auto map : map_to_erase) + VTR_ASSERT(maps.erase(map) == 1); + + int num_modes = maps.size(); + + VTR_ASSERT(num_modes > 0); + + pb_type->num_modes = num_modes; + pb_type->modes = new t_mode[num_modes]; + + int count = 0; + for (auto map : maps) { + if (map.site != site.getName()) + continue; + + int idx = count++; + t_mode* mode = &pb_type->modes[idx]; + auto name = str(map.cell); + mode->name = vtr::strdup(name.c_str()); + mode->parent_pb_type = pb_type; + mode->index = idx; + mode->num_pb_type_children = 1; + mode->pb_type_children = new t_pb_type[1]; + + auto leaf = &mode->pb_type_children[0]; + std::string leaf_name = name == std::string(pb_type->name) ? name + std::string("_leaf") : name; + leaf->name = vtr::strdup(leaf_name.c_str()); + leaf->num_pb = 1; + leaf->parent_mode = mode; + + // Pre-count pins + int ic_count = 0; + for (auto pin_map : map.pins) { + auto cell_pin = str(pin_map.first); + + if (cell_pin == arch_->vcc_cell || cell_pin == arch_->gnd_cell) + continue; + + ic_count++; + } + + int num_ports = ic_count; + leaf->num_ports = num_ports; + leaf->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + leaf->blif_model = vtr::strdup((std::string(".subckt ") + name).c_str()); + leaf->model = get_model(arch_, name); + + mode->num_interconnect = num_ports; + mode->interconnect = new t_interconnect[num_ports]; + std::set> pins; + ic_count = 0; + for (auto pin_map : map.pins) { + auto cell_pin = str(pin_map.first); + auto bel_pin = str(pin_map.second); + + if (cell_pin == arch_->vcc_cell || cell_pin == arch_->gnd_cell) + continue; + + std::smatch regex_matches; + std::string pin_suffix; + const std::regex port_regex("([0-9A-Za-z-]+)\\[([0-9]+)\\]"); + if (std::regex_match(cell_pin, regex_matches, port_regex)) { + cell_pin = regex_matches[1].str(); + pin_suffix = std::string("[") + regex_matches[2].str() + std::string("]"); + } + + auto model_port = get_model_port(arch_, name, cell_pin); + + auto size = model_port->size; + auto dir = model_port->dir; + + // Assign suffix to bel pin as it is a inout pin which was split in out and in ports + auto pin_reader = get_bel_pin_reader(site, bel, bel_pin); + bool is_inout = pin_reader.getDir() == INOUT; + + pins.emplace(cell_pin, dir, size); + + std::string istr, ostr, ic_name; + switch (dir) { + case IN_PORT: + bel_pin = is_inout ? bel_pin + in_suffix_ : bel_pin; + istr = pb_name + std::string(".") + bel_pin; + ostr = leaf_name + std::string(".") + cell_pin + pin_suffix; + break; + case OUT_PORT: + bel_pin = is_inout ? bel_pin + out_suffix_ : bel_pin; + istr = leaf_name + std::string(".") + cell_pin + pin_suffix; + ostr = pb_name + std::string(".") + bel_pin; + break; + default: + VTR_ASSERT(0); + } + + ic_name = istr + std::string("_") + ostr; + + auto ic = &mode->interconnect[ic_count++]; + ic->name = vtr::strdup(ic_name.c_str()); + ic->type = DIRECT_INTERC; + ic->parent_mode_index = idx; + ic->parent_mode = mode; + ic->input_string = vtr::strdup(istr.c_str()); + ic->output_string = vtr::strdup(ostr.c_str()); + } + + create_ports(leaf, pins, name); + } + } + + /** @brief Generates a routing block to allow for cascading routing blocks to be + * placed in the same complex block type. + */ + void process_routing_block(t_pb_type* pb_type) { + pb_type->num_modes = 1; + pb_type->modes = new t_mode[1]; + + int idx = 0; + auto mode = &pb_type->modes[idx]; + + std::string name = std::string(pb_type->name); + mode->name = vtr::strdup(name.c_str()); + mode->parent_pb_type = pb_type; + mode->index = idx; + mode->num_pb_type_children = 0; + + std::string istr, ostr, ic_name; + + // The MUX interconnections can only have a single output + VTR_ASSERT(pb_type->num_output_pins == 1); + + for (int iport = 0; iport < pb_type->num_ports; iport++) { + const t_port port = pb_type->ports[iport]; + auto port_name = name + "." + std::string(port.name); + switch (port.type) { + case IN_PORT: + istr += istr.empty() ? port_name : " " + port_name; + break; + case OUT_PORT: + ostr = port_name; + break; + default: + VTR_ASSERT(0); + } + } + + ic_name = std::string(pb_type->name); + + mode->num_interconnect = 1; + mode->interconnect = new t_interconnect[1]; + + e_interconnect ic_type = pb_type->num_input_pins == 1 ? DIRECT_INTERC : MUX_INTERC; + + auto ic = &mode->interconnect[idx]; + ic->name = vtr::strdup(ic_name.c_str()); + ic->type = ic_type; + ic->parent_mode_index = idx; + ic->parent_mode = mode; + ic->input_string = vtr::strdup(istr.c_str()); + ic->output_string = vtr::strdup(ostr.c_str()); + } + + /** @brief Processes all the ports of a given complex block. + * If a bel name index is specified, the bel pins are processed, otherwise the site ports + * are processed instead. + */ + void process_block_ports(t_pb_type* pb_type, Device::SiteType::Reader& site, size_t bel_name = OPEN) { + // Prepare data based on pb_type level + std::set> pins; + if (bel_name == (size_t)OPEN) { + for (auto pin : site.getPins()) { + auto dir = pin.getDir() == INPUT ? IN_PORT : OUT_PORT; + pins.emplace(str(pin.getName()), dir, 1); + } + } else { + auto bel = get_bel_reader(site, str(bel_name)); + + for (auto bel_pin : bel.getPins()) { + auto pin = site.getBelPins()[bel_pin]; + auto dir = pin.getDir(); + + switch (dir) { + case INPUT: + pins.emplace(str(pin.getName()), IN_PORT, 1); + break; + case OUTPUT: + pins.emplace(str(pin.getName()), OUT_PORT, 1); + break; + case INOUT: + pins.emplace(str(pin.getName()) + in_suffix_, IN_PORT, 1); + pins.emplace(str(pin.getName()) + out_suffix_, OUT_PORT, 1); + break; + default: + VTR_ASSERT(0); + } + } + } + + create_ports(pb_type, pins); + } + + /** @brief Generates all the port for a complex block, given its pointer and a map of ports (key) and their direction and width */ + void create_ports(t_pb_type* pb_type, std::set>& pins, std::string model = "") { + std::unordered_set names; + + 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_tuple : pins) { + std::string pin_name; + PORTS pin_dir; + int num_pins; + std::tie(pin_name, pin_dir, num_pins) = pin_tuple; + + if (pin_dir != dir) + continue; + + 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, /*string_model=*/"", num_pins); + ports[pin_count] = port; + port.index = pin_count++; + port.port_index_by_type = pins_dir_count++; + port.absolute_first_pin_index = pin_abs++; + + if (!model.empty()) + port.model_port = get_model_port(arch_, model, pin_name); + } + } + } + + /** @brief Processes and creates the interconnects corresponding to a given mode */ + void process_interconnects(t_mode* mode, Device::SiteType::Reader& site) { + auto ics = get_interconnects(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) { + auto ic_name = ic_pair.first; + auto ic_data = ic_pair.second; + + auto input = ic_data.input; + auto outputs = ic_data.outputs; + + auto merge_string = [](std::string ss, std::string s) { + return ss.empty() ? s : ss + " " + s; + }; + + std::string outputs_str = std::accumulate(outputs.begin(), outputs.end(), std::string(), merge_string); + + t_interconnect* ic = &mode->interconnect[curr_ic++]; + + // 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 = DIRECT_INTERC; + 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(input.c_str()); + ic->output_string = vtr::strdup(outputs_str.c_str()); + } + + // Checks and, in case, adds all the necessary pack patterns to the marked interconnects + for (size_t iic = 0; iic < num_ic; iic++) { + t_interconnect* ic = &mode->interconnect[iic]; + + auto ic_data = ics.at(std::string(ic->name)); + + if (ic_data.requires_pack_pattern) { + auto backward_pps_map = propagate_pack_patterns(ic, site, BACKWARD); + auto forward_pps_map = propagate_pack_patterns(ic, site, FORWARD); + + std::unordered_map> pps_map; + + for (auto pp : backward_pps_map) + pps_map.emplace(pp.first, std::set{}); + + for (auto pp : forward_pps_map) + pps_map.emplace(pp.first, std::set{}); + + // Cross-product of all pack-patterns added both when exploring backwards and forward. + // E.g.: + // Generated pack patterns + // - backward: OBUFDS, OBUF + // - forward: OPAD + // Final pack patterns: + // - OBUFDS_OPAD, OBUF_OPAD + for (auto for_pp_pair : forward_pps_map) + for (auto back_pp_pair : backward_pps_map) + for (auto for_pp : for_pp_pair.second) + for (auto back_pp : back_pp_pair.second) { + std::string pp_name = for_pp + "_" + back_pp; + pps_map.at(for_pp_pair.first).insert(pp_name); + pps_map.at(back_pp_pair.first).insert(pp_name); + } + + for (auto pair : pps_map) { + t_interconnect* pp_ic = pair.first; + + auto num_pp = pair.second.size(); + pp_ic->num_annotations = num_pp; + pp_ic->annotations = new t_pin_to_pin_annotation[num_pp]; + + int idx = 0; + for (auto pp_name : pair.second) + pp_ic->annotations[idx++] = get_pack_pattern(pp_name, pp_ic->input_string, pp_ic->output_string); + } + } + } + } + + /** @brief Propagates and generates all pack_patterns required for the given ic. + * This is necessary to find all root blocks that generate the pack pattern. + */ + std::unordered_map> propagate_pack_patterns(t_interconnect* ic, Device::SiteType::Reader& site, e_pp_dir direction) { + auto site_pins = site.getBelPins(); + + std::string endpoint = direction == BACKWARD ? ic->input_string : ic->output_string; + auto ic_endpoints = vtr::split(endpoint, " "); + + std::unordered_map> pps_map; + + bool is_backward = direction == BACKWARD; + + for (auto ep : ic_endpoints) { + auto parts = vtr::split(ep, "."); + auto bel = parts[0]; + auto pin = parts[1]; + + if (bel == str(site.getName())) + return pps_map; + + // Assign mode and pb_type + t_mode* parent_mode = ic->parent_mode; + t_pb_type* pb_type = nullptr; + + for (int ipb = 0; ipb < parent_mode->num_pb_type_children; ipb++) + if (std::string(parent_mode->pb_type_children[ipb].name) == bel) + pb_type = &parent_mode->pb_type_children[ipb]; + + VTR_ASSERT(pb_type != nullptr); + + auto bel_reader = get_bel_reader(site, remove_bel_suffix(bel)); + + // Passing through routing mux. Check at the muxes input pins interconnects + if (bel_reader.getCategory() == ROUTING) { + for (auto bel_pin : bel_reader.getPins()) { + auto pin_reader = site_pins[bel_pin]; + auto pin_name = str(pin_reader.getName()); + + if (pin_reader.getDir() != (is_backward ? INPUT : OUTPUT)) + continue; + + for (int iic = 0; iic < parent_mode->num_interconnect; iic++) { + t_interconnect* other_ic = &parent_mode->interconnect[iic]; + + if (std::string(ic->name) == std::string(other_ic->name)) + continue; + + std::string ic_to_find = bel + "." + pin_name; + + bool found = false; + for (auto out : vtr::split(is_backward ? other_ic->output_string : other_ic->input_string, " ")) + found |= out == ic_to_find; + + if (found) { + // An output interconnect to propagate was found, continue searching + auto res = propagate_pack_patterns(other_ic, site, direction); + + for (auto pp_map : res) + pps_map.emplace(pp_map.first, pp_map.second); + } + } + } + } else { + VTR_ASSERT(bel_reader.getCategory() == LOGIC); + + for (int imode = 0; imode < pb_type->num_modes; imode++) { + t_mode* mode = &pb_type->modes[imode]; + + for (int iic = 0; iic < mode->num_interconnect; iic++) { + t_interconnect* other_ic = &mode->interconnect[iic]; + + bool found = false; + for (auto other_ep : vtr::split(is_backward ? other_ic->output_string : other_ic->input_string, " ")) { + found |= other_ep == ep; + } + + if (found) { + std::string pp_name = std::string(pb_type->name) + "." + std::string(mode->name); + + std::set pp{pp_name}; + auto res = pps_map.emplace(other_ic, pp); + + if (!res.second) + res.first->second.insert(pp_name); + } + } + } + } + } + + return pps_map; + } + + // 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(); + auto siteTypeList = ar_.getSiteTypeList(); + + for (auto tile : tileTypeList) { + t_physical_tile_type ptype; + auto name = str(tile.getName()); + + if (name == EMPTY.name) + continue; + + bool has_valid_sites = false; + + for (auto site_type : tile.getSiteTypes()) + has_valid_sites |= take_sites_.count(siteTypeList[site_type.getPrimaryType()].getName()) != 0; + + if (!has_valid_sites) + continue; + + ptype.name = vtr::strdup(name.c_str()); + ptype.index = ++index; + ptype.width = ptype.height = ptype.area = 1; + ptype.capacity = 0; + + process_sub_tiles(ptype, tile); + + setup_pin_classes(&ptype); + + bool is_io = false; + for (auto site : tile.getSiteTypes()) { + auto site_type = ar_.getSiteTypeList()[site.getPrimaryType()]; + + for (auto bel : site_type.getBels()) + is_io |= is_pad(str(bel.getName())); + } + + ptype.is_input_type = ptype.is_output_type = is_io; + + ptype.switchblock_locations = vtr::Matrix({{1, 1}}, e_sb_type::FULL); + ptype.switchblock_switch_overrides = vtr::Matrix({{1, 1}}, DEFAULT_SWITCH); + + 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()]; + + if (take_sites_.count(site.getName()) == 0) + continue; + + auto pins_to_wires = site_in_tile.getPrimaryPinsToTileWires(); + + sub_tile.index = type.capacity; + sub_tile.name = vtr::strdup(str(site.getName()).c_str()); + sub_tile.capacity.set(type.capacity, type.capacity); + type.capacity++; + + int port_idx = 0; + int abs_first_pin_idx = 0; + int icount = 0; + int ocount = 0; + + std::unordered_map port_name_to_wire_name; + int idx = 0; + for (auto dir : {INPUT, 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_name_to_wire_name[std::string(port.name)] = str(pins_to_wires[idx++]); + + port.equivalent = PortEquivalence::NONE; + port.num_pins = 1; + + sub_tile.sub_tile_to_tile_pin_indices.push_back(type.num_pins + port_idx); + port.index = port_idx++; + + port.absolute_first_pin_index = abs_first_pin_idx++; + port.port_index_by_type = port_idx_by_type++; + + port.is_clock = false; + port.is_non_clock_global = false; + + if (dir == 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.num_pins += pins_size; + type.num_inst_pins += pins_size; + + type.num_input_pins += icount; + type.num_output_pins += ocount; + type.num_receivers += icount; + type.num_drivers += ocount; + + 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; + + // FIXME: Use always one segment for the time being. + // Can use the right segment for this IOPIN as soon + // as the RR graph reading from the interchange is complete. + 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); + } + } + + // Layout Processing + void process_layout() { + auto tiles = ar_.getTileList(); + auto tile_types = ar_.getTileTypeList(); + auto site_types = ar_.getSiteTypeList(); + + std::vector packages; + for (auto package : ar_.getPackages()) + packages.push_back(str(package.getName())); + + for (auto name : packages) { + t_grid_def grid_def; + grid_def.width = grid_def.height = 0; + for (auto tile : tiles) { + grid_def.width = std::max(grid_def.width, tile.getCol() + 1); + grid_def.height = std::max(grid_def.height, tile.getRow() + 1); + } + + grid_def.width += 2; + grid_def.height += 2; + + grid_def.grid_type = GridDefType::FIXED; + + 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 : tiles) { + auto tile_type = tile_types[tile.getType()]; + + bool found = false; + for (auto site : tile.getSites()) { + auto site_type_in_tile = tile_type.getSiteTypes()[site.getType()]; + auto site_type = site_types[site_type_in_tile.getPrimaryType()]; + + found |= take_sites_.count(site_type.getName()) != 0; + } + + if (!found) + continue; + + t_metadata_dict data; + std::string tile_prefix = str(tile.getName()); + std::string tile_type_name = str(tile_type.getName()); + + size_t pos = tile_prefix.find(tile_type_name); + if (pos != std::string::npos && pos == 0) + tile_prefix.erase(pos, tile_type_name.length() + 1); + t_grid_loc_def single(tile_type_name, 1); + single.x.start_expr = std::to_string(tile.getCol() + 1); + single.y.start_expr = std::to_string(tile.getRow() + 1); + + 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)); + } } void process_device() { @@ -377,19 +2144,18 @@ struct ArchReader { pip_timing_models_list.push_back(entry); } - auto num_switches = pip_timing_models.size() + 2; + size_t num_switches = pip_timing_models.size() + 2; 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]; + for (size_t i = 0; i < num_switches; ++i) { + t_arch_switch_inf* as = &arch_->Switches[i]; R = Cin = Cint = Cout = Tdel = 0.0; SwitchType type; @@ -437,32 +2203,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; @@ -473,17 +2237,25 @@ struct ArchReader { wire_names.insert(wires[pip.getWire1()]); } } - int num_seg = wire_names.size(); + + // FIXME: have only one segment type for the time being, so that + // the RR graph generation is correct. + // This can be removed once the RR graph reader from the interchange + // device is ready and functional. + size_t num_seg = 1; //wire_names.size(); + arch_->Segments.resize(num_seg); - uint32_t index = 0; + size_t index = 0; for (auto i : wire_names) { + if (index >= num_seg) break; + // 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; - arch_->Segments[index].Cmetal = 0; + arch_->Segments[index].Rmetal = 1e-12; + arch_->Segments[index].Cmetal = 1e-12; arch_->Segments[index].parallel_axis = BOTH_AXIS; // TODO: Only bi-directional segments are created, but it the interchange format @@ -497,6 +2269,7 @@ struct ArchReader { arch_->Segments[index].sb.resize(2); arch_->Segments[index].sb[0] = true; arch_->Segments[index].sb[1] = true; + segment_name_to_segment_idx[str(i)] = index; ++index; } } 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/libs/libvtrutil/src/vtr_util.cpp b/libs/libvtrutil/src/vtr_util.cpp index 45ee3035883..c897d296f0f 100644 --- a/libs/libvtrutil/src/vtr_util.cpp +++ b/libs/libvtrutil/src/vtr_util.cpp @@ -501,4 +501,34 @@ int get_pid() { #endif } +char* stringf(const char* format, ...) { + // Initial buffer + const int initial_size = 80 + 1; + char* str = (char*)vtr::malloc(initial_size); + + // Init and copy args list + va_list va1, va2; + va_start(va1, format); + va_copy(va2, va1); + + // First attempt + int len = vsnprintf(str, initial_size, format, va1); + VTR_ASSERT(len >= 0); + + // The buffer was too small + if (len >= initial_size) { + str = (char*)vtr::realloc((void*)str, len + 1); + VTR_ASSERT(str != nullptr); + len = vsnprintf(str, len + 1, format, va2); + VTR_ASSERT(len >= 0); + VTR_ASSERT(len <= len); + } + + // Cleanup + va_end(va1); + va_end(va2); + + return str; +} + } // namespace vtr diff --git a/libs/libvtrutil/src/vtr_util.h b/libs/libvtrutil/src/vtr_util.h index 08562d3d092..3d482eb96bb 100644 --- a/libs/libvtrutil/src/vtr_util.h +++ b/libs/libvtrutil/src/vtr_util.h @@ -118,6 +118,8 @@ void uniquify(Container container) { int get_pid(); +char* stringf(const char* format, ...); + } // namespace vtr #endif 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/read_circuit.cpp b/vpr/src/base/read_circuit.cpp index 04ca872cdec..e29b8cc6239 100644 --- a/vpr/src/base/read_circuit.cpp +++ b/vpr/src/base/read_circuit.cpp @@ -1,6 +1,7 @@ #include "read_circuit.h" #include "read_blif.h" #include "read_interchange_netlist.h" +#include "write_interchange_netlist.h" #include "atom_netlist.h" #include "atom_netlist_utils.h" #include "echo_files.h" @@ -11,6 +12,8 @@ #include "vtr_path.h" #include "vtr_time.h" +#include "read_options.h" + static void process_circuit(AtomNetlist& netlist, e_const_gen_inference const_gen_inference_method, bool should_absorb_buffers, @@ -22,9 +25,11 @@ static void process_circuit(AtomNetlist& netlist, static void show_circuit_stats(const AtomNetlist& netlist); -AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, t_vpr_setup& vpr_setup, t_arch& arch) { +AtomNetlist read_and_process_circuit(const t_options& options, t_vpr_setup& vpr_setup, t_arch& arch) { // Options - const char* circuit_file = vpr_setup.PackerOpts.circuit_file_name.c_str(); + const char* rd_circuit_file = vpr_setup.PackerOpts.circuit_file_name.c_str(); + const std::string& wr_circuit_file = options.write_circuit_file; + e_circuit_format circuit_format = options.circuit_format; const t_model* user_models = vpr_setup.user_models; const t_model* library_models = vpr_setup.library_models; e_const_gen_inference const_gen_inference = vpr_setup.NetlistOpts.const_gen_inference; @@ -36,16 +41,16 @@ AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, t_vpr_setu bool verbosity = vpr_setup.NetlistOpts.netlist_verbosity; if (circuit_format == e_circuit_format::AUTO) { - auto name_ext = vtr::split_ext(circuit_file); + auto name_ext = vtr::split_ext(rd_circuit_file); - VTR_LOGV(verbosity, "Circuit file: %s\n", circuit_file); + VTR_LOGV(verbosity, "Circuit file: %s\n", rd_circuit_file); if (name_ext[1] == ".blif") { circuit_format = e_circuit_format::BLIF; } else if (name_ext[1] == ".eblif") { circuit_format = e_circuit_format::EBLIF; } else { VPR_FATAL_ERROR(VPR_ERROR_ATOM_NETLIST, "Failed to determine file format for '%s' expected .blif or .eblif extension", - circuit_file); + rd_circuit_file); } } @@ -56,15 +61,15 @@ AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, t_vpr_setu switch (circuit_format) { case e_circuit_format::BLIF: case e_circuit_format::EBLIF: - netlist = read_blif(circuit_format, circuit_file, user_models, library_models); + netlist = read_blif(circuit_format, rd_circuit_file, user_models, library_models); break; case e_circuit_format::FPGA_INTERCHANGE: - netlist = read_interchange_netlist(circuit_file, arch); + netlist = read_interchange_netlist(rd_circuit_file, arch); break; default: VPR_FATAL_ERROR(VPR_ERROR_ATOM_NETLIST, "Unable to identify circuit file format for '%s'. Expect [blif|eblif|fpga-interchange]!\n", - circuit_file); + rd_circuit_file); break; } } @@ -86,6 +91,14 @@ AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, t_vpr_setu print_netlist_as_blif(getEchoFileName(E_ECHO_ATOM_NETLIST_CLEANED), netlist); } + // Write FPGA interchange logical netlist. + // TODO: Possibly allow writing BLIF/ELBIF via --write_circuit too + // TODO: Possibly make separate options for read and write circuit format + if (circuit_format == e_circuit_format::FPGA_INTERCHANGE && !wr_circuit_file.empty()) { + vtr::ScopedStartFinishTimer t("Write circuit"); + write_interchange_netlist(wr_circuit_file, netlist, arch); + } + show_circuit_stats(netlist); return netlist; diff --git a/vpr/src/base/read_circuit.h b/vpr/src/base/read_circuit.h index 90be01a3891..41525567a40 100644 --- a/vpr/src/base/read_circuit.h +++ b/vpr/src/base/read_circuit.h @@ -4,6 +4,8 @@ #include "atom_netlist_fwd.h" #include "vpr_types.h" +struct t_options; + enum class e_circuit_format { AUTO, /// init; for (auto entry : props) { - if (std::string(str_list[entry.getKey()].cStr()) != init_param) + if (str_list[entry.getKey()] != init_param) continue; // TODO: export this to a library function to have generic parameter decoding if (entry.which() == LogicalNetlist::Netlist::PropertyMap::Entry::TEXT_VALUE) { const std::regex vhex_regex("[0-9]+'h([0-9A-Z]+)"); - const std::regex vbit_regex("[0-9]+'b([0-9A-Z]+)"); + const std::regex vbit_regex("[0-9]+'b([0-9]+)"); + const std::regex chex_regex("0x([0-9A-Za-z]+)"); + const std::regex cbit_regex("0b([0-9]+)"); const std::regex bit_regex("[0-1]+"); - std::string init_str = std::string(str_list[entry.getTextValue()].cStr()); + std::string init_str = str_list[entry.getTextValue()]; std::smatch regex_matches; // Fill the init vector @@ -180,9 +182,18 @@ struct NetlistReader { for (int bit = 3; bit >= 0; bit--) init.push_back((value >> bit) & 1); } + else if (std::regex_match(init_str, regex_matches, chex_regex)) + for (const char& c : regex_matches[1].str()) { + int value = std::stoi(std::string(1, c), 0, 16); + for (int bit = 3; bit >= 0; bit--) + init.push_back((value >> bit) & 1); + } else if (std::regex_match(init_str, regex_matches, vbit_regex)) for (const char& c : regex_matches[1].str()) init.push_back((bool)std::stoi(std::string(1, c), 0, 2)); + else if (std::regex_match(init_str, regex_matches, cbit_regex)) + for (const char& c : regex_matches[1].str()) + init.push_back((bool)std::stoi(std::string(1, c), 0, 2)); else if (std::regex_match(init_str, regex_matches, bit_regex)) for (const char& c : init_str) init.push_back((bool)std::stoi(std::string(1, c), 0, 2)); @@ -249,7 +260,7 @@ struct NetlistReader { std::unordered_map port_net_map; for (auto net : top_cell.getNets()) { - std::string net_name = str_list[net.getName()].cStr(); + std::string net_name = str_list[net.getName()]; for (auto port : net.getPortInsts()) { if (!port.isInst() || port.getInst() != inst_idx) continue; @@ -260,6 +271,9 @@ struct NetlistReader { int inum = 0; for (auto port : cell_lib.getPorts()) { + if (port_net_map.find(port) == port_net_map.end()) + continue; + auto net_name = port_net_map.at(port); AtomNetId net_id = main_netlist_.create_net(net_name); @@ -291,7 +305,7 @@ struct NetlistReader { auto cell = decl_list[inst_list[cell_inst].getCell()]; bool is_lut; - std::tie(is_lut, std::ignore, std::ignore) = is_lut_cell(str_list[cell.getName()].cStr()); + std::tie(is_lut, std::ignore, std::ignore) = is_lut_cell(str_list[cell.getName()]); if (!is_lut) insts.emplace_back(cell_inst, inst_list[cell_inst].getCell()); @@ -301,10 +315,10 @@ struct NetlistReader { auto inst_idx = inst_pair.first; auto cell_idx = inst_pair.second; - auto model_name = str_list[decl_list[cell_idx].getName()].cStr(); + auto model_name = str_list[decl_list[cell_idx].getName()]; const t_model* blk_model = find_model(model_name); - std::string inst_name = str_list[inst_list[inst_idx].getName()].cStr(); + std::string inst_name = str_list[inst_list[inst_idx].getName()]; VTR_ASSERT(inst_name.empty() == 0); //The name for every block should be unique, check that there is no name conflict @@ -320,9 +334,9 @@ struct NetlistReader { auto port_net_map = get_port_net_map(inst_idx); auto cell = decl_list[inst_list[inst_idx].getCell()]; - if (std::string(str_list[cell.getName()].cStr()) == arch_.vcc_cell) + if (str_list[cell.getName()] == arch_.vcc_cell) inst_name = arch_.vcc_cell; - else if (std::string(str_list[cell.getName()].cStr()) == arch_.gnd_cell) + else if (str_list[cell.getName()] == arch_.gnd_cell) inst_name = arch_.gnd_cell; if (main_netlist_.find_block(inst_name)) @@ -343,10 +357,10 @@ struct NetlistReader { net_name = arch_.gnd_net; auto port = port_list[port_idx]; - auto port_name = str_list[port.getName()].cStr(); + auto port_name = str_list[port.getName()]; //Check for consistency between model and ports - const t_model_ports* model_port = find_model_port(blk_model, std::string(port_name)); + const t_model_ports* model_port = find_model_port(blk_model, port_name); VTR_ASSERT(model_port); //Determine the pin type @@ -443,7 +457,7 @@ struct NetlistReader { auto str_list = nr_.getStrList(); std::unordered_map, std::string, vtr::hash_pair> map; for (auto net : top_cell.getNets()) { - std::string net_name = str_list[net.getName()].cStr(); + std::string net_name = str_list[net.getName()]; for (auto port : net.getPortInsts()) { if (!port.isInst() || port.getInst() != inst_idx) continue; diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index 45ed25c4faa..1c6f6b949e0 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1486,6 +1486,10 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg .help("Writes the placement delay lookup to the specified file.") .show_in(argparse::ShowIn::HELP_ONLY); + file_grp.add_argument(args.write_circuit_file, "--write_circuit") + .help("Writes the circuit netlist used for placement and routing to the specified file.") + .show_in(argparse::ShowIn::HELP_ONLY); + file_grp.add_argument(args.out_file_prefix, "--outfile_prefix") .help("Prefix for output files") .show_in(argparse::ShowIn::HELP_ONLY); diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 7e8fed79752..d77035a16fa 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -36,6 +36,8 @@ struct t_options { argparse::ArgValue write_router_lookahead; argparse::ArgValue read_router_lookahead; + argparse::ArgValue write_circuit_file; + /* Stage Options */ argparse::ArgValue do_packing; argparse::ArgValue do_placement; diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 3b3b8dbe15e..7a36baf76a4 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -313,7 +313,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a /* Read blif file and sweep unused components */ auto& atom_ctx = g_vpr_ctx.mutable_atom(); - atom_ctx.nlist = read_and_process_circuit(options->circuit_format, *vpr_setup, *arch); + atom_ctx.nlist = read_and_process_circuit(*options, *vpr_setup, *arch); if (vpr_setup->PowerOpts.do_power) { //Load the net activity file for power estimation 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/src/base/write_interchange_netlist.cpp b/vpr/src/base/write_interchange_netlist.cpp new file mode 100644 index 00000000000..b9e39aeaac7 --- /dev/null +++ b/vpr/src/base/write_interchange_netlist.cpp @@ -0,0 +1,532 @@ +/** + * @file + * @brief FPGA Interchange Netlist writer + * + */ +#include "atom_netlist.h" + +#include "write_interchange_netlist.h" + +#include "LogicalNetlist.capnp.h" +#include "capnp/serialize.h" +#include "capnp/serialize-packed.h" + +#include + +// ============================================================================ + +class NetlistBuilder { + public: + using Direction = ::LogicalNetlist::Netlist::Direction; + + // .................................................... + + NetlistBuilder(const AtomNetlist& netlist, const t_arch& arch) + : netlist_(netlist) + , arch_(arch) {} + + void build(LogicalNetlist::Netlist::Builder& logical_netlist) { + logical_netlist.setName(netlist_.netlist_name()); + + // Build and serialize cell library + buildLibrary(logical_netlist); + + // Build and serialize the netlist + buildNetlist(logical_netlist); + + // Serialize the string list + auto strList = logical_netlist.initStrList(strings_.size()); + for (const auto& it : strings_) { + strList.set(it.second, it.first); + } + } + + // .................................................... + + protected: + typedef std::unordered_map Properties; + + struct Port { + uint32_t index; + Direction dir; + bool isBus; + uint32_t bitLo, bitHi; + Properties properties; + }; + + struct PortInstance { + std::string cell; + std::string port; + uint32_t bit; + bool isTop; + }; + + struct Net { + std::unordered_map> ports; + Properties properties; + }; + + struct CellDeclaration { + uint32_t index; + std::vector ports; + Properties properties; + }; + + struct CellInstance { + uint32_t index; + std::string type; + std::string name; + Properties properties; + }; + + struct Cell { + std::vector instances; + std::unordered_set nets; + }; + + // .................................................... + + const AtomNetlist& netlist_; + const t_arch& arch_; + + std::unordered_map strings_; + + std::unordered_map, Port, vtr::hash_pair> ports_; + std::unordered_map nets_; + std::unordered_map cellDeclarations_; + std::unordered_map cellInstances_; + + // .................................................... + + void buildLibrary(LogicalNetlist::Netlist::Builder& logical_netlist) { + // Collect models and their ports + std::unordered_map models; + for (const auto blk : netlist_.blocks()) { + const t_model* model = netlist_.block_model(blk); + VTR_ASSERT(model != nullptr); + + // Skip models representing top-level IO ports + if (isIoModel(model->name)) { + continue; + } + + addCellDeclaration(model); + } + + // Build top-level cell declaration. This is not a real cell in the + // netlist but it represents all top-level ports + buildTopLevelCellDeclaration(); + + // Serialize ports + auto portList = logical_netlist.initPortList(ports_.size()); + for (const auto& it : ports_) { + const auto& port = it.second; + portList[port.index].setName(strId(it.first.second)); + portList[port.index].setDir(port.dir); + if (port.isBus) { + auto bus = portList[port.index].initBus(); + bus.setBusStart(port.bitLo); + bus.setBusEnd(port.bitHi); + } + + auto propMap = portList[port.index].initPropMap(); + buildPropertyMap(propMap, port.properties); + } + + // Serialize cell definitions + auto cellDecls = logical_netlist.initCellDecls(cellDeclarations_.size()); + for (const auto& it : cellDeclarations_) { + const auto& decl = it.second; + cellDecls[decl.index].setName(strId(it.first)); + cellDecls[decl.index].setView(strId("netlist")); + cellDecls[decl.index].setLib(strId("work")); + auto ports = cellDecls[decl.index].initPorts(decl.ports.size()); + for (size_t i = 0; i < decl.ports.size(); ++i) { + const auto& port = ports_.at(std::make_pair(it.first, decl.ports[i])); + ports.set(i, port.index); + } + + auto propMap = cellDecls[decl.index].initPropMap(); + buildPropertyMap(propMap, decl.properties); + } + } + + void buildNetlist(LogicalNetlist::Netlist::Builder& logical_netlist) { + // Add cell instances + for (const auto blk : netlist_.blocks()) { + const t_model* model = netlist_.block_model(blk); + VTR_ASSERT(model != nullptr); + + // Skip models representing top-level IO ports + if (isIoModel(model->name)) { + continue; + } + + addCellInstance(blk); + } + + // Add nets + for (const auto net : netlist_.nets()) { + addNet(net); + } + + // Serialize cell instances + auto instList = logical_netlist.initInstList(cellInstances_.size()); + for (const auto& it : cellInstances_) { + const auto& inst = it.second; + + instList[inst.index].setName(strId(inst.name)); + instList[inst.index].setView(strId("netlist")); + instList[inst.index].setCell(cellDeclarations_.at(inst.type).index); + + auto propMap = instList[inst.index].initPropMap(); + buildPropertyMap(propMap, inst.properties); + } + + // Serialize top-level instance + auto topInst = logical_netlist.initTopInst(); + topInst.setName(strId(netlist_.netlist_name())); + topInst.setView(strId("netlist")); + topInst.setCell(cellDeclarations_.at(netlist_.netlist_name()).index); + // TODO: Prop map + + // Group cell instances by their types + std::unordered_map groups; + for (const auto& it : cellInstances_) { + const auto& inst = it.second; + + // Add new / get existing group + auto& group = groups[inst.type]; + group.instances.push_back(inst.index); + } + + // Add net connections to the grouped instances + for (const auto& it_net : nets_) { + const auto& name = it_net.first; + const auto& net = it_net.second; + + for (const auto& it_port : net.ports) { + const std::string& type = it_port.first; + //const auto& instances = it_port.second; + + if (isIoModel(type.c_str())) { + continue; + } + + VTR_ASSERT(groups.find(type) != groups.end()); + auto& group = groups.at(type); + group.nets.insert(name); + } + } + + // Serialize groups + auto cellList = logical_netlist.initCellList(groups.size()); + size_t groupIndex = 0; + for (const auto& it : groups) { + const auto type = it.first; + const auto& group = it.second; + + cellList[groupIndex].setIndex(cellDeclarations_.at(type).index); + + // Instances + auto insts = cellList[groupIndex].initInsts(group.instances.size()); + for (size_t i = 0; i < group.instances.size(); ++i) { + insts.set(i, group.instances[i]); + } + + // Nets + auto nets = cellList[groupIndex].initNets(group.nets.size()); + size_t netIndex = 0; + for (const auto& net_name : group.nets) { + VTR_ASSERT(nets_.find(net_name) != nets_.end()); + const auto& net = nets_.at(net_name); + + nets[netIndex].setName(strId(net_name)); + + // Collect port instances. Include any top-level ports too. + auto ports = net.ports.at(type); + if (net.ports.count(MODEL_INPUT)) { + const auto& p = net.ports.at(MODEL_INPUT); + ports.insert(ports.end(), p.begin(), p.end()); + } + if (net.ports.count(MODEL_OUTPUT)) { + const auto& p = net.ports.at(MODEL_OUTPUT); + ports.insert(ports.end(), p.begin(), p.end()); + } + + // Serialize + auto portInsts = nets[netIndex].initPortInsts(ports.size()); + for (size_t i = 0; i < ports.size(); ++i) { + auto key = ports[i].isTop ? std::make_pair(netlist_.netlist_name(), net_name) : std::make_pair(type, ports[i].port); + + const auto& port = ports_.at(key); + portInsts[i].setPort(port.index); + + if (!ports[i].isTop) { + portInsts[i].setInst(cellInstances_.at(ports[i].cell).index); + } else { + portInsts[i].setExtPort(); + } + + if (port.isBus) { + auto busIdx = portInsts[i].initBusIdx(); + busIdx.setIdx(ports[i].bit); + } + } + + // Property map + auto propMap = nets[netIndex].initPropMap(); + buildPropertyMap(propMap, net.properties); + + // Next net + netIndex++; + } + + // Next group + groupIndex++; + } + } + + void buildPropertyMap(::LogicalNetlist::Netlist::PropertyMap::Builder& propertyMap, const Properties& properties) { + auto entries = propertyMap.initEntries(properties.size()); + size_t index = 0; + + for (const auto& it : properties) { + entries[index].setKey(strId(it.first)); + entries[index].setTextValue(strId(it.second)); // FIXME: VPR keeps all attributes / parameters as strings + index++; + } + } + + // .................................................... + + static bool isIoModel(const char* str) { + return !strcmp(str, MODEL_INPUT) || !strcmp(str, MODEL_OUTPUT); + } + + uint32_t strId(const std::string& str) { + if (strings_.count(str)) { + return strings_[str]; + } else { + uint32_t id = strings_.size(); + strings_[str] = id; + return id; + } + } + + void addCellDeclaration(const t_model* model) { + // Already exists + auto it = cellDeclarations_.find(model->name); + if (it != cellDeclarations_.end()) { + return; + } + + // Create a new cell declaration from the model + CellDeclaration decl; + + // Collect ports + for (t_model_ports* p = model->inputs; p; p = p->next) { + addPort(model->name, p, Direction::INPUT); + decl.ports.push_back(p->name); + } + for (t_model_ports* p = model->outputs; p; p = p->next) { + addPort(model->name, p, Direction::OUTPUT); + decl.ports.push_back(p->name); + } + + // Cell definition properties + // FIXME: Should those information be written as properties? What + // should be the mapping of those attributes to interchange properties ? + if (model->never_prune) { + decl.properties["never_prune"] = "1"; + } + + // Add it + decl.index = cellDeclarations_.size(); + cellDeclarations_.emplace(std::make_pair(model->name, decl)); + } + + void addPort(const std::string& modelName, const t_model_ports* modelPort, Direction dir) { + // Already exists + auto it = ports_.find(std::make_pair(modelName, modelPort->name)); + if (it != ports_.end()) { + return; + } + + // Build the port + Port port; + port.dir = dir; + + if (modelPort->size > 1) { + port.isBus = true; + port.bitLo = 0; + port.bitHi = modelPort->size - 1; + } else { + port.isBus = false; + port.bitLo = 0; + port.bitHi = 0; + } + + // Port properties + // FIXME: Should those information be written as properties? What + // should be the mapping of those attributes to interchange properties ? + if (modelPort->is_clock) { + port.properties["is_clock"] = "1"; + } + if (modelPort->is_non_clock_global) { + port.properties["is_non_clock_global"] = "1"; + } + + // Add the port + port.index = ports_.size(); + ports_.emplace(std::make_pair(std::make_pair(modelName, modelPort->name), port)); + } + + uint32_t addCellInstance(const AtomBlockId& block) { + const t_model* model = netlist_.block_model(block); + VTR_ASSERT(model != nullptr); + + // Create the cell instance + CellInstance inst; + inst.name = netlist_.block_name(block); + inst.type = model->name; + + // Collect attributes and parameters + auto addProperty = [&](const std::pair& prop) { + if (inst.properties.count(prop.first)) { + VTR_LOG_WARN("Duplicate property '%s'='%s' of cell instance '%s'\n", + prop.first.c_str(), prop.second.c_str(), inst.name.c_str()); + } else { + inst.properties.insert(prop); + } + }; + + // FIXME: The FPGA Interchange logical netlist format does not + // differentiate between attributes and parameters. + for (const auto& it : netlist_.block_attrs(block)) { + addProperty(it); + } + for (const auto& it : netlist_.block_params(block)) { + addProperty(it); + } + + // Add the cell instance + inst.index = cellInstances_.size(); + VTR_ASSERT(cellInstances_.count(inst.name) == 0); + cellInstances_.emplace(std::make_pair(inst.name, inst)); + + return inst.index; + } + + void addNet(const AtomNetId& net_id) { + Net net; + + // Handle driver / endpoints + for (const auto& pin_id : netlist_.net_pins(net_id)) { + // Block + const auto& blk_id = netlist_.pin_block(pin_id); + const auto& blk_name = netlist_.block_name(blk_id); + + // Port / bit + const auto& port_id = netlist_.pin_port(pin_id); + const auto& port_bit = netlist_.pin_port_bit(pin_id); + const auto& port_name = netlist_.port_name(port_id); + + // Find the cell declaration + const t_model* model = netlist_.block_model(blk_id); + VTR_ASSERT(model != nullptr); + + PortInstance portInst; + portInst.cell = blk_name; + portInst.port = port_name; + portInst.bit = port_bit; + portInst.isTop = isIoModel(model->name); + + net.ports[model->name].push_back(portInst); + } + + // Add the net + const auto& name = netlist_.net_name(net_id); + VTR_ASSERT(nets_.count(name) == 0); + nets_.emplace(std::make_pair(name, net)); + } + + uint32_t buildTopLevelCellDeclaration() { + CellDeclaration decl; + + // Collect top-level ports + for (const auto blk_id : netlist_.blocks()) { + const t_model* model = netlist_.block_model(blk_id); + VTR_ASSERT(model != nullptr); + + // Skip non-io + if (!isIoModel(model->name)) { + continue; + } + + std::string port_name = netlist_.block_name(blk_id); + Port port; + + if (!strcmp(model->name, MODEL_INPUT)) { + VTR_ASSERT(netlist_.block_output_pins(blk_id).size() == 1); + port.dir = Direction::INPUT; + } else if (!strcmp(model->name, MODEL_OUTPUT)) { + VTR_ASSERT(netlist_.block_input_pins(blk_id).size() == 1); + port.dir = Direction::OUTPUT; + } else { + VTR_ASSERT(false); + } + + // Skip the "out:" prefix if present + auto pos = port_name.find("out:"); + if (pos != std::string::npos) { + port_name = port_name.substr(pos + 4); + } + + // TODO: Potentially identify top-level buses and aggregate them. + port.isBus = false; + port.bitLo = 0; + port.bitHi = 0; + + // Add the port + port.index = ports_.size(); + ports_.emplace(std::make_pair(std::make_pair(netlist_.netlist_name(), port_name), port)); + + // Add the port to the cell declaration + decl.ports.push_back(port_name); + } + + // Add the declaration + decl.index = cellDeclarations_.size(); + cellDeclarations_.emplace(std::make_pair(netlist_.netlist_name(), decl)); + + return decl.index; + } +}; + +// ============================================================================ + +void write_interchange_netlist( + const std::string& ic_netlist_file, + const AtomNetlist& netlist, + const t_arch& arch) { + // Initialize the builder + ::capnp::MallocMessageBuilder message; + LogicalNetlist::Netlist::Builder logical_netlist = message.initRoot(); + + // Build the netlist + auto builder = NetlistBuilder(netlist, arch); + builder.build(logical_netlist); + + // Serialize + kj::Array words = messageToFlatArray(message); + kj::ArrayPtr bytes = words.asBytes(); + + // Write + gzFile fp = gzopen(ic_netlist_file.c_str(), "w"); + VTR_ASSERT(fp != Z_NULL); + + z_size_t res = gzwrite(fp, &bytes[0], bytes.size()); + VTR_ASSERT(res == (z_size_t)bytes.size()); + VTR_ASSERT(gzclose(fp) == Z_OK); +} diff --git a/vpr/src/base/write_interchange_netlist.h b/vpr/src/base/write_interchange_netlist.h new file mode 100644 index 00000000000..b240832c1e7 --- /dev/null +++ b/vpr/src/base/write_interchange_netlist.h @@ -0,0 +1,13 @@ +#ifndef WRITE_INTERCHANGE_NETLIST_H +#define WRITE_INTERCHANGE_NETLIST_H + +#include "logic_types.h" +#include "atom_netlist_fwd.h" +#include "vpr_types.h" + +void write_interchange_netlist( + const std::string& ic_netlist_file, + const AtomNetlist& netlist, + const t_arch& arch); + +#endif /*WRITE_INTERCHANGE_NETLIST_H*/ diff --git a/vpr/src/pack/pb_type_graph.cpp b/vpr/src/pack/pb_type_graph.cpp index 7f855cb9ab6..40552ba778c 100644 --- a/vpr/src/pack/pb_type_graph.cpp +++ b/vpr/src/pack/pb_type_graph.cpp @@ -1098,9 +1098,6 @@ static bool realloc_and_load_pb_graph_pin_ptrs_at_var(const int line_num, return false; //clb[9:0]123 } (*token_index)++; - if (!checkTokenType(tokens[*token_index], TOKEN_STRING)) { - return false; //clb[9:0].123 - } /* parse ports and port pins of pb */ port_name = tokens[*token_index].data; diff --git a/vpr/test/lut.netlist b/vpr/test/lut.netlist index d8a49d6feab..7f2833b66d1 100644 Binary files a/vpr/test/lut.netlist and b/vpr/test/lut.netlist differ diff --git a/vpr/test/test_interchange_device.cpp b/vpr/test/test_interchange_device.cpp index 49302728d19..22a04eaca1e 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", "DFFR", "DFFS", "GND", "VCC"}; // Check that there are exactly the expected models for (auto* model = arch.models; model != nullptr; model = model->next) { @@ -50,10 +50,10 @@ TEST_CASE("read_interchange_layout", "[vpr]") { auto& gd = arch.grid_layouts[0]; REQUIRE(gd.grid_type == GridDefType::FIXED); - REQUIRE(gd.height == 10); - REQUIRE(gd.width == 10); + REQUIRE(gd.height == 12); + REQUIRE(gd.width == 12); - std::unordered_map tile_types({{"NULL", false}, {"PWR", false}, {"IOB", false}, {"CLB", false}}); + std::unordered_map tile_types({{"PWR", false}, {"IOB", false}, {"CLB", false}}); for (auto& loc : gd.loc_defs) { auto ret = tile_types.find(loc.block_type); REQUIRE(ret != tile_types.end()); @@ -67,4 +67,125 @@ 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_cells = {"LUT1", "LUT2", "LUT3", "LUT4"}; + std::unordered_set lut_bels = {"ALUT", "BLUT"}; + std::unordered_set lut_cell_pins = {"I0", "I1", "I2", "I3"}; + std::unordered_set lut_bel_pins = {"A1", "A2", "A3", "A4"}; + + REQUIRE(arch.lut_cells.size() == 4); + for (auto lut_cell : arch.lut_cells) { + CHECK(std::find(lut_cells.begin(), lut_cells.end(), lut_cell.name) != lut_cells.end()); + REQUIRE(lut_cell.init_param == std::string("INIT")); + for (auto lut_pin : lut_cell.inputs) + CHECK(std::find(lut_cell_pins.begin(), lut_cell_pins.end(), lut_pin) != lut_cell_pins.end()); + } + + for (const auto& it : arch.lut_elements) { + const auto& lut_elements = it.second; + + for (const auto& lut_element : lut_elements) { + REQUIRE(lut_element.lut_bels.size() == 2); + + for (auto lut_bel : lut_element.lut_bels) { + CHECK(std::find(lut_bels.begin(), lut_bels.end(), lut_bel.name) != lut_bels.end()); + REQUIRE(lut_bel.output_pin == std::string("O")); + for (auto lut_pin : lut_bel.input_pins) + CHECK(std::find(lut_bel_pins.begin(), lut_bel_pins.end(), lut_pin) != lut_bel_pins.end()); + } + } + } +} + +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_0", PORTS::IN_PORT}, + {"L1_0", PORTS::IN_PORT}, + {"L2_0", PORTS::IN_PORT}, + {"L3_0", PORTS::IN_PORT}, + {"R_0", PORTS::IN_PORT}, + {"D_0", PORTS::IN_PORT}, + {"O_0", PORTS::OUT_PORT}, + {"Q_0", PORTS::OUT_PORT}, + {"L0_1", PORTS::IN_PORT}, + {"L1_1", PORTS::IN_PORT}, + {"L2_1", PORTS::IN_PORT}, + {"L3_1", PORTS::IN_PORT}, + {"R_1", PORTS::IN_PORT}, + {"D_1", PORTS::IN_PORT}, + {"O_1", PORTS::OUT_PORT}, + {"Q_1", PORTS::OUT_PORT}, + {"CLK", PORTS::IN_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/test_interchange_netlist.cpp b/vpr/test/test_interchange_netlist.cpp index 2af1b33a96f..b38c92987e9 100644 --- a/vpr/test/test_interchange_netlist.cpp +++ b/vpr/test/test_interchange_netlist.cpp @@ -1,5 +1,6 @@ #include "catch2/catch_test_macros.hpp" +#include "argparse_value.hpp" #include "read_circuit.h" #include "read_fpga_interchange_arch.h" #include "arch_util.h" @@ -25,9 +26,12 @@ TEST_CASE("read_interchange_netlist", "[vpr]") { vpr_setup.library_models = arch.model_library; vpr_setup.PackerOpts.circuit_file_name = "lut.netlist"; + t_options options; + options.circuit_format.set(e_circuit_format::FPGA_INTERCHANGE, argparse::Provenance::SPECIFIED); + /* Read blif file and sweep unused components */ auto& atom_ctx = g_vpr_ctx.mutable_atom(); - atom_ctx.nlist = read_and_process_circuit(e_circuit_format::FPGA_INTERCHANGE, vpr_setup, arch); + atom_ctx.nlist = read_and_process_circuit(options, vpr_setup, arch); } } // namespace diff --git a/vpr/test/testarch.device b/vpr/test/testarch.device index e9b12b1ac89..92c32afe1c4 100644 Binary files a/vpr/test/testarch.device and b/vpr/test/testarch.device differ