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 8b789bfb9d4..22319ffb063 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -1732,9 +1732,23 @@ struct t_clock_arch_spec { std::vector clock_connections_arch; }; +struct t_lut_cell { + std::string name; + std::string init_param; + std::vector inputs; +}; + +struct t_lut_bel { + std::string name; + + std::vector input_pins; + std::string output_pin; +}; + /* 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 @@ -1750,11 +1764,24 @@ struct t_arch { int num_switches; t_direct_inf* Directs = nullptr; int num_directs = 0; + t_model* models = nullptr; t_model* model_library = nullptr; + t_power_arch* power = nullptr; t_clock_arch* clocks = nullptr; + // Constants + std::string gnd_cell; + std::string vcc_cell; + + std::string gnd_net = "$__gnd_net"; + std::string vcc_net = "$__vcc_net"; + + // Luts + std::vector lut_cells; + std::vector lut_bels; + //The name of the switch used for the input connection block (i.e. to //connect routing tracks to block pins). //This should correspond to a switch in Switches diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 5aa9181003f..153ae4e6a37 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,19 +36,197 @@ using namespace DeviceResources; using namespace LogicalNetlist; using namespace capnp; +struct t_package_pin { + std::string name; + + std::string site_name; + std::string bel_name; +}; + +struct t_bel_cell_mapping { + int cell; + std::vector> pins; +}; + +/****************** Utility functions ******************/ + +static float get_corner_value(Device::CornerModel::Reader model, const char* speed_model, const char* value) { + bool slow_model = std::string(speed_model) == std::string("slow"); + bool fast_model = std::string(speed_model) == std::string("fast"); + + bool min_corner = std::string(value) == std::string("min"); + bool typ_corner = std::string(value) == std::string("typ"); + bool max_corner = std::string(value) == std::string("max"); + + if (!slow_model && !fast_model) { + archfpga_throw("", __LINE__, + "Wrong speed model `%s`. Expected `slow` or `fast`\n", speed_model); + } + + if (!min_corner && !typ_corner && !max_corner) { + archfpga_throw("", __LINE__, + "Wrong corner model `%s`. Expected `min`, `typ` or `max`\n", value); + } + + bool has_fast = model.getFast().hasFast(); + bool has_slow = model.getSlow().hasSlow(); + + if (slow_model && has_slow) { + auto half = model.getSlow().getSlow(); + if (min_corner && half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (typ_corner && half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (max_corner && half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + if (half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + archfpga_throw("", __LINE__, + "Invalid speed model %s. No value found!\n", speed_model); + } + } + } else if (fast_model && has_fast) { + auto half = model.getFast().getFast(); + if (min_corner && half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (typ_corner && half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (max_corner && half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + if (half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + archfpga_throw("", __LINE__, + "Invalid speed model %s. No value found!\n", speed_model); + } + } + } + + return 0.; +} + +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; +} + +static t_model* get_model(t_arch* arch, std::string model) { + for (t_model* m : {arch->models, arch->model_library}) + for (; m != nullptr; m = m->next) + if (std::string(m->name) == model) + return m; + + archfpga_throw(__FILE__, __LINE__, + "Could not find model: %s\n", model.c_str()); +} + +template +static T* get_type_by_name(const char* type_name, std::vector& types) { + for (auto& type : types) { + if (0 == strcmp(type.name, type_name)) { + return &type; + } + } + + archfpga_throw(__FILE__, __LINE__, + "Could not find type: %s\n", type_name); +} + +static t_port get_generic_port(t_arch* arch, + t_pb_type* pb_type, + PORTS dir, + std::string name, + std::string model = "", + int num_pins = 1) { + t_port port; + port.parent_pb_type = pb_type; + port.name = vtr::strdup(name.c_str()); + port.num_pins = num_pins; + port.index = 0; + port.absolute_first_pin_index = 0; + port.port_index_by_type = 0; + port.equivalent = PortEquivalence::NONE; + port.type = dir; + port.is_clock = false; + port.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; +} + +/****************** 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_models(); + process_device(); + + process_blocks(); + process_tiles(); + link_physical_logical_types(ptypes_, ltypes_); + + SyncModelsPbTypes(arch_, ltypes_); + check_models(arch_); + + process_layout(); + process_switches(); + process_segments(); } private: @@ -58,6 +238,290 @@ struct ArchReader { t_default_fc_spec default_fc_; + // Package pins + + // TODO: add possibility to have multiple packages + std::vector pad_bels_; + + // Bel Cell mappings + std::unordered_map> bel_cell_mappings_; + + // Utils + std::string str(int idx) { + return arch_->interned_strings[idx].get(&arch_->strings); + } + + int get_bel_type_count(Device::SiteType::Reader& site, Device::BELCategory category) { + int count = 0; + for (auto bel : site.getBels()) + if (bel.getCategory() == category) + count++; + + return count; + } + + Device::BEL::Reader get_bel_reader(Device::SiteType::Reader& site, std::string bel_name) { + for (auto bel : site.getBels()) { + if (str(bel.getName()) == bel_name) + return bel; + } + VTR_ASSERT(0); + } + + std::string get_ic_prefix(Device::SiteType::Reader& site, Device::BEL::Reader& bel) { + return bel.getCategory() == Device::BELCategory::SITE_PORT ? str(site.getName()) : str(bel.getName()); + } + + bool is_lut(std::string name) { + for (auto cell : arch_->lut_cells) + if (cell.name == name) + return true; + + for (auto bel : arch_->lut_bels) + if (bel.name == name) + return true; + + return false; + } + + bool is_pad(std::string name) { + for (auto pad : pad_bels_) + if (pad.bel_name == name) + return true; + + return false; + } + + std::unordered_map> get_ics(Device::SiteType::Reader& site) { + // dictionary: + // - key: interconnect name + // - value: (inputs string, outputs string, interconnect type) + std::unordered_map> ics; + for (auto wire : site.getSiteWires()) { + std::string wire_name = str(wire.getName()); + + // pin name, bel name + std::tuple out_pin; + bool is_mux = false; + for (auto pin : wire.getPins()) { + auto bel_pin = site.getBelPins()[pin]; + auto bel = get_bel_reader(site, str(bel_pin.getBel())); + auto bel_name = get_ic_prefix(site, bel); + + bool is_output = bel_pin.getDir() == LogicalNetlist::Netlist::Direction::OUTPUT; + if (is_output) { + VTR_ASSERT(std::get<1>(out_pin).empty()); + out_pin = std::make_tuple(pin, str(bel_pin.getName()), bel_name); + is_mux = bel.getCategory() == Device::BELCategory::ROUTING; + } + } + + VTR_ASSERT(!std::get<1>(out_pin).empty()); + + // Stores all output BELs connected to the same out_pin + std::string pad_bel_name; + std::string pad_bel_pin_name; + bool is_pad = false; + for (auto pin : wire.getPins()) { + if (pin == std::get<0>(out_pin)) + continue; + + auto bel_pin = site.getBelPins()[pin]; + std::string out_bel_pin_name = str(bel_pin.getName()); + + auto bel = get_bel_reader(site, str(bel_pin.getBel())); + auto out_bel_name = get_ic_prefix(site, bel); + + for (auto pad_bel : pad_bels_) { + is_pad = pad_bel.bel_name == out_bel_name || is_pad; + pad_bel_name = pad_bel.bel_name == out_bel_name ? out_bel_name : pad_bel_name; + pad_bel_pin_name = pad_bel.bel_name == out_bel_name ? out_bel_pin_name : pad_bel_pin_name; + } + } + + for (auto pin : wire.getPins()) { + if (pin == std::get<0>(out_pin)) + continue; + + auto bel_pin = site.getBelPins()[pin]; + std::string out_bel_pin_name = str(bel_pin.getName()); + + auto bel = get_bel_reader(site, str(bel_pin.getBel())); + auto out_bel_name = get_ic_prefix(site, bel); + + auto in_bel_name = std::get<2>(out_pin); + auto in_bel_pin_name = std::get<1>(out_pin); + + std::string ostr = out_bel_name + "." + out_bel_pin_name; + std::string istr = in_bel_name + "." + in_bel_pin_name; + + std::string inputs; + std::string outputs; + e_interconnect ic_type; + if (is_mux) { + auto ic_name = in_bel_name; + auto res = ics.emplace(ic_name, std::make_tuple(std::string(), ostr, MUX_INTERC, false)); + + if (!res.second) { + std::tie(inputs, outputs, ic_type, std::ignore) = res.first->second; + outputs += " " + ostr; + res.first->second = std::make_tuple(inputs, outputs, ic_type, false); + } + } else if (bel.getCategory() == Device::BELCategory::ROUTING) { + auto ic_name = str(bel.getName()); + auto res = ics.emplace(ic_name, std::make_tuple(istr, std::string(), MUX_INTERC, false)); + + if (!res.second) { + std::tie(inputs, outputs, ic_type, std::ignore) = res.first->second; + inputs += " " + istr; + res.first->second = std::make_tuple(inputs, outputs, ic_type, false); + } + } else { + auto ic_name = wire_name + "_" + out_bel_pin_name; + if (is_pad && bel.getCategory() == Device::BELCategory::LOGIC) { + if (out_bel_name == pad_bel_name) + ostr += "_in"; + else { // Create new wire to connect PAD output to the BELs input + ic_name = wire_name + "_" + pad_bel_pin_name + "_out"; + istr = pad_bel_name + "." + pad_bel_pin_name + "_out"; + } + } + + auto res = ics.emplace(ic_name, std::make_tuple(istr, ostr, DIRECT_INTERC, is_pad)); + + if (!res.second) { + std::string ins, outs; + std::tie(ins, outs, ic_type, std::ignore) = res.first->second; + + VTR_ASSERT(ins == istr); + + if (outs.empty()) + outs = ostr; + else + outs += " " + ostr; + + res.first->second = std::make_tuple(ins, outs, ic_type, is_pad); + } + } + } + } + + return ics; + } + + /** + * Preprocessors: + * - 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 + */ + + 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()) { + for (auto bel : lut.getBels()) { + t_lut_bel lut_bel; + + std::string name = bel.getName().cStr(); + lut_bel.name = name; + + // Check for duplicates + auto is_duplicate = [name](t_lut_bel l) { return l.name == name; }; + auto res = std::find_if(arch_->lut_bels.begin(), arch_->lut_bels.end(), is_duplicate); + if (res != arch_->lut_bels.end()) + continue; + + std::vector ipins; + for (auto pin : bel.getInputPins()) + ipins.push_back(pin.cStr()); + + lut_bel.input_pins = ipins; + lut_bel.output_pin = bel.getOutputPin().cStr(); + + arch_->lut_bels.push_back(lut_bel); + } + } + } + } + + void process_package_pins() { + for (auto package : ar_.getPackages()) { + for (auto pin : package.getPackagePins()) { + t_package_pin pckg_pin; + pckg_pin.name = str(pin.getPackagePin()); + + if (pin.getBel().isBel()) + pckg_pin.bel_name = str(pin.getBel().getBel()); + + if (pin.getSite().isSite()) + pckg_pin.site_name = str(pin.getSite().getSite()); + + pad_bels_.push_back(pckg_pin); + } + } + } + + void process_cell_bel_mappings() { + for (auto cell_mapping : ar_.getCellBelMap()) { + int cell_name = cell_mapping.getCell(); + + 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()) { + for (auto bel : site_type_entry.getBels()) { + t_bel_cell_mapping mapping; + + mapping.cell = cell_name; + mapping.pins = pins; + + std::vector maps{mapping}; + + auto res = bel_cell_mappings_.emplace(bel, maps); + if (!res.second) + res.first->second.push_back(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 @@ -70,16 +534,21 @@ 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; + try { temp = new t_model; temp->index = model_index++; temp->never_prune = true; - temp->name = vtr::strdup(std::string(strList[primitive.getName()]).c_str()); + temp->name = vtr::strdup(str(primitive.getName()).c_str()); + ret_map_name = model_name_map.insert(std::pair(temp->name, 0)); if (!ret_map_name.second) { archfpga_throw(arch_file_, __LINE__, @@ -100,11 +569,9 @@ struct ArchReader { arch_->models = temp; } } - return; } void process_model_ports(t_model* model, Netlist::CellDeclaration::Reader primitive) { - auto strList = ar_.getStrList(); auto primLib = ar_.getPrimLibs(); auto portList = primLib.getPortList(); @@ -128,7 +595,7 @@ struct ArchReader { } t_model_ports* model_port = new t_model_ports; model_port->dir = dir; - model_port->name = vtr::strdup(std::string(strList[port.getName()]).c_str()); + model_port->name = vtr::strdup(str(port.getName()).c_str()); // TODO: add parsing of clock port types when the interchange schema allows for it: // https://github.com/chipsalliance/fpga-interchange-schema/issues/66 @@ -151,6 +618,14 @@ struct ArchReader { "Model output ports can not have combinational sink ports"); } + model_port->min_size = 1; + model_port->size = 1; + if (port.isBus()) { + int s = port.getBus().getBusStart(); + int e = port.getBus().getBusEnd(); + model_port->size = std::abs(e - s) + 1; + } + port_names.insert(std::pair(model_port->name, dir)); //Add the port if (dir == IN_PORT) { @@ -163,6 +638,860 @@ struct ArchReader { } } } + + // Complex Blocks + void process_blocks() { + auto siteTypeList = ar_.getSiteTypeList(); + + int index = 0; + auto EMPTY = get_empty_logical_type(std::string("NULL")); + EMPTY.index = index; + ltypes_.push_back(EMPTY); + + for (auto site : siteTypeList) { + t_logical_block_type ltype; + + std::string name = str(site.getName()); + + // Check for duplicates + auto is_duplicate = [name](t_logical_block_type l) { return std::string(l.name) == name; }; + VTR_ASSERT(std::find_if(ltypes_.begin(), ltypes_.end(), is_duplicate) == ltypes_.end()); + + ltype.name = vtr::strdup(name.c_str()); + ltype.index = ++index; + + auto pb_type = new t_pb_type; + ltype.pb_type = pb_type; + + pb_type->name = vtr::strdup(name.c_str()); + pb_type->num_pb = 1; + process_block_ports(pb_type, site); + + // Process modes (for simplicity, only the default mode is allowed for the time being) + pb_type->num_modes = 1; + pb_type->modes = new t_mode[pb_type->num_modes]; + + auto bels = site.getBels(); + auto mode = &pb_type->modes[0]; + mode->parent_pb_type = pb_type; + mode->index = 0; + mode->name = vtr::strdup("default"); + mode->disable_packing = false; + + int bel_count = get_bel_type_count(site, Device::BELCategory::LOGIC); + mode->num_pb_type_children = bel_count; + mode->pb_type_children = new t_pb_type[bel_count]; + + int count = 0; + for (auto bel : bels) { + if (bel.getCategory() != Device::BELCategory::LOGIC) + continue; + + auto bel_name = str(bel.getName()); + std::pair key(name, bel_name); + + auto mid_pb_type = new t_pb_type; + mid_pb_type->name = vtr::strdup(bel_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, false); + + if (is_lut(bel_name)) + process_lut_block(mid_pb_type); + else if (is_pad(bel_name)) + process_pad_block(mid_pb_type, bel, site); + else + process_generic_block(mid_pb_type, bel); + + mode->pb_type_children[count++] = *mid_pb_type; + } + + process_interconnects(mode, site); + ltypes_.push_back(ltype); + } + } + + void process_lut_block(t_pb_type* lut) { + lut->num_modes = 1; + lut->modes = new t_mode[1]; + + // Check for duplicates + std::string lut_name = lut->name; + auto find_lut = [lut_name](t_lut_bel l) { return l.name == lut_name; }; + auto res = std::find_if(arch_->lut_bels.begin(), arch_->lut_bels.end(), find_lut); + VTR_ASSERT(res != arch_->lut_bels.end()); + auto lut_bel = *res; + + auto mode = &lut->modes[0]; + mode->name = vtr::strdup("lut"); + mode->parent_pb_type = lut; + mode->index = 0; + mode->num_pb_type_children = 1; + mode->pb_type_children = new t_pb_type[1]; + + auto new_leaf = new t_pb_type; + new_leaf->name = vtr::strdup("lut_child"); + new_leaf->num_pb = 1; + new_leaf->parent_mode = mode; + + int num_ports = 2; + new_leaf->num_ports = num_ports; + new_leaf->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + new_leaf->blif_model = vtr::strdup(MODEL_NAMES); + new_leaf->model = get_model(arch_, std::string(MODEL_NAMES)); + + auto in_size = lut_bel.input_pins.size(); + new_leaf->ports[0] = get_generic_port(arch_, new_leaf, IN_PORT, "in", MODEL_NAMES, in_size); + new_leaf->ports[1] = get_generic_port(arch_, new_leaf, OUT_PORT, "out", MODEL_NAMES); + + mode->pb_type_children[0] = *new_leaf; + + // Num inputs + 1 (output pin) + int num_pins = in_size + 1; + + mode->num_interconnect = num_pins; + mode->interconnect = new t_interconnect[num_pins]; + + for (int i = 0; i < num_pins; i++) { + auto ic = new t_interconnect; + + std::stringstream istr; + std::stringstream ostr; + std::string input_string; + std::string output_string; + + if (i < num_pins - 1) { + istr << lut_bel.input_pins[i]; + ostr << "in[" << i << "]"; + input_string = std::string(lut->name) + std::string(".") + istr.str(); + output_string = std::string(new_leaf->name) + std::string(".") + ostr.str(); + } else { + istr << "out"; + ostr << lut_bel.output_pin; + input_string = std::string(new_leaf->name) + std::string(".") + istr.str(); + output_string = std::string(lut->name) + std::string(".") + ostr.str(); + } + std::string name = istr.str() + std::string("_") + ostr.str(); + ic->name = vtr::strdup(name.c_str()); + ic->type = DIRECT_INTERC; + ic->parent_mode_index = 0; + ic->parent_mode = mode; + ic->input_string = vtr::strdup(input_string.c_str()); + ic->output_string = vtr::strdup(output_string.c_str()); + + mode->interconnect[i] = *ic; + } + } + + void process_pad_block(t_pb_type* pad, Device::BEL::Reader& bel, Device::SiteType::Reader& site) { + // For now, hard-code two modes for pads, so that PADs can either be IPADs or OPADs + pad->num_modes = 2; + pad->modes = new t_mode[2]; + + // Add PAD pb_type ports + VTR_ASSERT(bel.getPins().size() == 1); + std::string pin = str(site.getBelPins()[bel.getPins()[0]].getName()); + std::string ipin = pin + "_in"; + std::string opin = pin + "_out"; + + auto num_ports = 2; + auto ports = new t_port[num_ports]; + pad->ports = ports; + pad->num_ports = pad->num_pins = num_ports; + pad->num_input_pins = 1; + pad->num_output_pins = 1; + + int pin_abs = 0; + int pin_count = 0; + for (auto dir : {IN_PORT, OUT_PORT}) { + int pins_dir_count = 0; + t_port* port = &ports[pin_count]; + + port->parent_pb_type = pad; + port->index = pin_count++; + port->port_index_by_type = pins_dir_count++; + port->absolute_first_pin_index = pin_abs++; + + port->equivalent = PortEquivalence::NONE; + port->num_pins = 1; + port->type = dir; + port->is_clock = false; + + bool is_input = dir == IN_PORT; + port->name = is_input ? vtr::strdup(ipin.c_str()) : vtr::strdup(opin.c_str()); + port->model_port = nullptr; + port->port_class = vtr::strdup(nullptr); + port->port_power = (t_port_power*)vtr::calloc(1, sizeof(t_port_power)); + } + + // OPAD mode + auto omode = &pad->modes[0]; + omode->name = vtr::strdup("opad"); + omode->parent_pb_type = pad; + omode->index = 0; + omode->num_pb_type_children = 1; + omode->pb_type_children = new t_pb_type[1]; + + auto opad = new t_pb_type; + opad->name = vtr::strdup("opad"); + opad->num_pb = 1; + opad->parent_mode = omode; + + num_ports = 1; + opad->num_ports = num_ports; + opad->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + opad->blif_model = vtr::strdup(MODEL_OUTPUT); + opad->model = get_model(arch_, std::string(MODEL_OUTPUT)); + + opad->ports[0] = get_generic_port(arch_, opad, IN_PORT, "outpad", MODEL_OUTPUT); + omode->pb_type_children[0] = *opad; + + // IPAD mode + auto imode = &pad->modes[1]; + imode->name = vtr::strdup("ipad"); + imode->parent_pb_type = pad; + imode->index = 1; + imode->num_pb_type_children = 1; + imode->pb_type_children = new t_pb_type[1]; + + auto ipad = new t_pb_type; + ipad->name = vtr::strdup("ipad"); + ipad->num_pb = 1; + ipad->parent_mode = imode; + + num_ports = 1; + ipad->num_ports = num_ports; + ipad->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); + ipad->blif_model = vtr::strdup(MODEL_INPUT); + ipad->model = get_model(arch_, std::string(MODEL_INPUT)); + + ipad->ports[0] = get_generic_port(arch_, ipad, OUT_PORT, "inpad", MODEL_INPUT); + imode->pb_type_children[0] = *ipad; + + // Handle interconnects + int num_pins = 1; + + omode->num_interconnect = num_pins; + omode->interconnect = new t_interconnect[num_pins]; + + imode->num_interconnect = num_pins; + imode->interconnect = new t_interconnect[num_pins]; + + std::string opad_istr = std::string(pad->name) + std::string(".") + ipin; + std::string opad_ostr = std::string(opad->name) + std::string(".outpad"); + std::string o_ic_name = std::string(pad->name) + std::string("_") + std::string(opad->name); + + std::string ipad_istr = std::string(ipad->name) + std::string(".inpad"); + std::string ipad_ostr = std::string(pad->name) + std::string(".") + opin; + std::string i_ic_name = std::string(ipad->name) + std::string("_") + std::string(pad->name); + + auto o_ic = new t_interconnect[num_pins]; + auto i_ic = new t_interconnect[num_pins]; + + o_ic->name = vtr::strdup(o_ic_name.c_str()); + o_ic->type = DIRECT_INTERC; + o_ic->parent_mode_index = 0; + o_ic->parent_mode = omode; + o_ic->input_string = vtr::strdup(opad_istr.c_str()); + o_ic->output_string = vtr::strdup(opad_ostr.c_str()); + + i_ic->name = vtr::strdup(i_ic_name.c_str()); + i_ic->type = DIRECT_INTERC; + i_ic->parent_mode_index = 0; + i_ic->parent_mode = imode; + i_ic->input_string = vtr::strdup(ipad_istr.c_str()); + i_ic->output_string = vtr::strdup(ipad_ostr.c_str()); + + omode->interconnect[0] = *o_ic; + imode->interconnect[0] = *i_ic; + } + + void process_generic_block(t_pb_type* pb_type, Device::BEL::Reader& bel) { + std::string pb_name = std::string(pb_type->name); + + auto maps = bel_cell_mappings_[bel.getName()]; + int num_modes = maps.size(); + + pb_type->num_modes = num_modes; + pb_type->modes = new t_mode[num_modes]; + + int count = 0; + for (auto map : maps) { + int idx = count++; + auto 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; + + int num_ports = map.pins.size(); + 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::unordered_map> pins; + int 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, false); + + if (model_port == nullptr) + continue; + + auto size = model_port->size; + auto dir = model_port->dir; + + pins.emplace(cell_pin, std::make_pair(dir, size)); + + std::string istr, ostr, ic_name; + switch (dir) { + case IN_PORT: + istr = pb_name + std::string(".") + bel_pin; + ostr = leaf_name + std::string(".") + cell_pin + pin_suffix; + break; + case OUT_PORT: + 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); + } + } + + void process_block_ports(t_pb_type* pb_type, Device::SiteType::Reader& site, bool is_root = true) { + // Prepare data based on pb_type level + std::unordered_map> pins; + if (is_root) { + for (auto pin : site.getPins()) { + auto dir = pin.getDir() == LogicalNetlist::Netlist::Direction::INPUT ? IN_PORT : OUT_PORT; + pins.emplace(str(pin.getName()), std::make_pair(dir, 1)); + } + } else { + for (auto bel : site.getBels()) { + if (bel.getCategory() != Device::BELCategory::LOGIC) + continue; + + if (std::string(pb_type->name) != str(bel.getName())) + continue; + + for (auto bel_pin : bel.getPins()) { + auto pin = site.getBelPins()[bel_pin]; + auto dir = pin.getDir() == LogicalNetlist::Netlist::Direction::INPUT ? IN_PORT : OUT_PORT; + pins.emplace(str(pin.getName()), std::make_pair(dir, 1)); + } + } + } + + create_ports(pb_type, pins); + } + + void create_ports(t_pb_type* pb_type, std::unordered_map>& 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_pair : pins) { + auto pin_name = pin_pair.first; + PORTS pin_dir; + int num_pins; + std::tie(pin_dir, num_pins) = pin_pair.second; + + if (pin_dir != dir) + continue; + + VTR_ASSERT(names.insert(pin_name).second); + + bool is_input = dir == IN_PORT; + pb_type->num_input_pins += is_input ? 1 : 0; + pb_type->num_output_pins += is_input ? 0 : 1; + + auto port = get_generic_port(arch_, pb_type, dir, pin_name, /*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); + } + } + } + + void process_interconnects(t_mode* mode, Device::SiteType::Reader& site) { + auto ics = get_ics(site); + auto num_ic = ics.size(); + + mode->num_interconnect = num_ic; + mode->interconnect = new t_interconnect[num_ic]; + + int curr_ic = 0; + std::unordered_set names; + + // Handle site wires, namely direct interconnects + for (auto ic_pair : ics) { + std::string ic_name = ic_pair.first; + + std::string inputs; + std::string outputs; + e_interconnect ic_type; + bool add_pack_pattern; + + std::tie(inputs, outputs, ic_type, add_pack_pattern) = ic_pair.second; + + t_interconnect* ic = &mode->interconnect[curr_ic++]; + + if (add_pack_pattern) { + ic->num_annotations = 1; + // pack pattern + auto pp = new t_pin_to_pin_annotation; + + pp->prop = (int*)vtr::calloc(1, sizeof(int)); + pp->value = (char**)vtr::calloc(1, sizeof(char*)); + + pp->type = E_ANNOT_PIN_TO_PIN_PACK_PATTERN; + pp->format = E_ANNOT_PIN_TO_PIN_CONSTANT; + pp->prop[0] = (int)E_ANNOT_PIN_TO_PIN_PACK_PATTERN_NAME; + pp->value[0] = vtr::strdup(ic_name.c_str()); + pp->input_pins = vtr::strdup(inputs.c_str()); + pp->output_pins = vtr::strdup(outputs.c_str()); + pp->num_value_prop_pairs = 1; + pp->clock = nullptr; + ic->annotations = pp; + } + + // No line num for interconnects, as line num is XML specific + // TODO: probably line_num should be deprecated as it is dependent + // on the input architecture format. + ic->line_num = 0; + ic->type = ic_type; + ic->parent_mode_index = mode->index; + ic->parent_mode = mode; + + VTR_ASSERT(names.insert(ic_name).second); + ic->name = vtr::strdup(ic_name.c_str()); + ic->input_string = vtr::strdup(inputs.c_str()); + ic->output_string = vtr::strdup(outputs.c_str()); + } + } + + // Physical Tiles + void process_tiles() { + auto EMPTY = get_empty_physical_type(std::string("NULL")); + int index = 0; + EMPTY.index = index; + ptypes_.push_back(EMPTY); + + auto tileTypeList = ar_.getTileTypeList(); + + for (auto tile : tileTypeList) { + t_physical_tile_type ptype; + auto name = str(tile.getName()); + + if (name == std::string("NULL")) + continue; + + ptype.name = vtr::strdup(name.c_str()); + ptype.index = ++index; + ptype.width = ptype.height = ptype.area = 1; + ptype.capacity = 1; + + process_sub_tiles(ptype, tile); + + setup_pin_classes(&ptype); + + bool is_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())) || is_io; + } + + ptype.is_input_type = ptype.is_output_type = is_io; + + ptypes_.push_back(ptype); + } + } + + void process_sub_tiles(t_physical_tile_type& type, Device::TileType::Reader& tile) { + // TODO: only one subtile at the moment + auto siteTypeList = ar_.getSiteTypeList(); + for (auto site_in_tile : tile.getSiteTypes()) { + t_sub_tile sub_tile; + + auto site = siteTypeList[site_in_tile.getPrimaryType()]; + + sub_tile.index = 0; + sub_tile.name = vtr::strdup(str(site.getName()).c_str()); + sub_tile.capacity.set(0, 0); + + int port_idx = 0; + int abs_first_pin_idx = 0; + int icount = 0; + int ocount = 0; + for (auto dir : {LogicalNetlist::Netlist::Direction::INPUT, LogicalNetlist::Netlist::Direction::OUTPUT}) { + int port_idx_by_type = 0; + for (auto pin : site.getPins()) { + if (pin.getDir() != dir) + continue; + + t_physical_tile_port port; + + port.name = vtr::strdup(str(pin.getName()).c_str()); + port.equivalent = PortEquivalence::NONE; + port.num_pins = 1; + + sub_tile.sub_tile_to_tile_pin_indices.push_back(port_idx); + port.index = port_idx++; + + port.absolute_first_pin_index = abs_first_pin_idx++; + port.port_index_by_type = port_idx_by_type++; + + if (dir == LogicalNetlist::Netlist::Direction::INPUT) { + port.type = IN_PORT; + icount++; + } else { + port.type = OUT_PORT; + ocount++; + } + + sub_tile.ports.push_back(port); + } + } + + auto pins_size = site.getPins().size(); + sub_tile.num_phy_pins += pins_size * type.capacity; + type.num_pins += pins_size * type.capacity; + type.num_inst_pins += pins_size; + + type.num_input_pins += icount; + type.num_output_pins += ocount; + type.num_receivers += icount * type.capacity; + type.num_drivers += ocount * type.capacity; + + type.pin_width_offset.resize(type.num_pins, 0); + type.pin_height_offset.resize(type.num_pins, 0); + + type.pinloc.resize({1, 1, 4}, std::vector(type.num_pins, false)); + for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { + for (int pin = 0; pin < type.num_pins; pin++) { + type.pinloc[0][0][side][pin] = true; + type.pin_width_offset[pin] = 0; + type.pin_height_offset[pin] = 0; + } + } + + auto ltype = get_type_by_name(sub_tile.name, ltypes_); + vtr::bimap directs_map; + + for (int npin = 0; npin < type.num_pins; npin++) { + t_physical_pin physical_pin(npin); + t_logical_pin logical_pin(npin); + + directs_map.insert(logical_pin, physical_pin); + } + + sub_tile.equivalent_sites.push_back(ltype); + + type.tile_block_pin_directs_map[ltype->index][sub_tile.index] = directs_map; + + // Assign FC specs + int iblk_pin = 0; + for (const auto& port : sub_tile.ports) { + t_fc_specification fc_spec; + + fc_spec.seg_index = 0; + + //Apply type and defaults + if (port.type == IN_PORT) { + fc_spec.fc_type = e_fc_type::IN; + fc_spec.fc_value_type = default_fc_.in_value_type; + fc_spec.fc_value = default_fc_.in_value; + } else { + VTR_ASSERT(port.type == OUT_PORT); + fc_spec.fc_type = e_fc_type::OUT; + fc_spec.fc_value_type = default_fc_.out_value_type; + fc_spec.fc_value = default_fc_.out_value; + } + + //Add all the pins from this port + for (int iport_pin = 0; iport_pin < port.num_pins; ++iport_pin) { + int true_physical_blk_pin = sub_tile.sub_tile_to_tile_pin_indices[iblk_pin++]; + fc_spec.pins.push_back(true_physical_blk_pin); + } + + type.fc_specs.push_back(fc_spec); + } + + type.sub_tiles.push_back(sub_tile); + } + } + + // Layout Processing + void process_layout() { + auto tileList = ar_.getTileList(); + auto tileTypeList = ar_.getTileTypeList(); + + 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 : tileList) { + grid_def.width = std::max(grid_def.width, tile.getCol() + 1); + grid_def.height = std::max(grid_def.height, tile.getRow() + 1); + } + + grid_def.grid_type = GridDefType::FIXED; + + if (name == "auto") { + archfpga_throw(arch_file_, __LINE__, + "The name auto is reserved for auto-size layouts; please choose another name"); + } + grid_def.name = name; + for (auto tile : tileList) { + t_metadata_dict data; + std::string tile_prefix = str(tile.getName()); + auto tileType = tileTypeList[tile.getType()]; + std::string tile_type = str(tileType.getName()); + + size_t pos = tile_prefix.find(tile_type); + if (pos != std::string::npos && pos == 0) + tile_prefix.erase(pos, tile_type.length() + 1); + t_grid_loc_def single(tile_type, 1); + single.x.start_expr = std::to_string(tile.getCol()); + single.y.start_expr = std::to_string(tile.getRow()); + + single.x.end_expr = single.x.start_expr + " + w - 1"; + single.y.end_expr = single.y.start_expr + " + h - 1"; + + single.owned_meta = std::make_unique(data); + single.meta = single.owned_meta.get(); + grid_def.loc_defs.emplace_back(std::move(single)); + } + + arch_->grid_layouts.emplace_back(std::move(grid_def)); + } + } + + void process_device() { + /* + * The generic architecture data is not currently available in the interchange format + * therefore, for a very initial implementation, the values are taken from the ones + * used primarly in the Xilinx series7 devices, generated using SymbiFlow. + * + * As the interchange format develops further, with possibly more details, this function can + * become dynamic, allowing for different parameters for the different architectures. + */ + arch_->R_minW_nmos = 6065.520020; + arch_->R_minW_pmos = 18138.500000; + arch_->grid_logic_tile_area = 14813.392; + arch_->Chans.chan_x_dist.type = UNIFORM; + arch_->Chans.chan_x_dist.peak = 1; + arch_->Chans.chan_x_dist.width = 0; + arch_->Chans.chan_x_dist.xpeak = 0; + arch_->Chans.chan_x_dist.dc = 0; + arch_->Chans.chan_y_dist.type = UNIFORM; + arch_->Chans.chan_y_dist.peak = 1; + arch_->Chans.chan_y_dist.width = 0; + arch_->Chans.chan_y_dist.xpeak = 0; + arch_->Chans.chan_y_dist.dc = 0; + arch_->ipin_cblock_switch_name = std::string("generic"); + arch_->SBType = WILTON; + arch_->Fs = 3; + default_fc_.specified = true; + default_fc_.in_value_type = e_fc_value_type::FRACTIONAL; + default_fc_.in_value = 1.0; + default_fc_.out_value_type = e_fc_value_type::FRACTIONAL; + default_fc_.out_value = 1.0; + } + + void process_switches() { + std::set> pip_timing_models; + for (auto tile_type : ar_.getTileTypeList()) { + for (auto pip : tile_type.getPips()) { + pip_timing_models.insert(std::pair(pip.getBuffered21(), pip.getTiming())); + if (!pip.getDirectional()) + pip_timing_models.insert(std::pair(pip.getBuffered20(), pip.getTiming())); + } + } + + auto timing_data = ar_.getPipTimings(); + + std::vector> pip_timing_models_list; + pip_timing_models_list.reserve(pip_timing_models.size()); + + for (auto entry : pip_timing_models) { + pip_timing_models_list.push_back(entry); + } + + auto num_switches = pip_timing_models.size() + 2; + std::string switch_name; + + arch_->num_switches = num_switches; + + if (num_switches > 0) { + 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 = &arch_->Switches[i]; + + R = Cin = Cint = Cout = Tdel = 0.0; + SwitchType type; + + if (i == 0) { + switch_name = "short"; + type = SwitchType::SHORT; + R = 0.0; + } else if (i == 1) { + switch_name = "generic"; + type = SwitchType::MUX; + R = 0.0; + } else { + auto entry = pip_timing_models_list[i - 2]; + auto model = timing_data[entry.second]; + std::stringstream name; + std::string mux_type_string = entry.first ? "mux_" : "passGate_"; + name << mux_type_string; + + R = get_corner_value(model.getOutputResistance(), "slow", "min"); + name << "R" << std::scientific << R; + + Cin = get_corner_value(model.getInputCapacitance(), "slow", "min"); + name << "Cin" << std::scientific << Cin; + + Cout = get_corner_value(model.getOutputCapacitance(), "slow", "min"); + name << "Cout" << std::scientific << Cout; + + if (entry.first) { + Cint = get_corner_value(model.getInternalCapacitance(), "slow", "min"); + name << "Cinternal" << std::scientific << Cint; + } + + Tdel = get_corner_value(model.getInternalDelay(), "slow", "min"); + name << "Tdel" << std::scientific << Tdel; + + switch_name = name.str(); + type = entry.first ? SwitchType::MUX : SwitchType::PASS_GATE; + } + + /* Should never happen */ + if (switch_name == std::string(VPR_DELAYLESS_SWITCH_NAME)) { + archfpga_throw(arch_file_, __LINE__, + "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.; + } else { + as->buf_size_type = BufferSize::AUTO; + as->buf_size = 0.; + as->power_buffer_type = POWER_BUFFER_TYPE_AUTO; + } + } + } + + void process_segments() { + // Segment names will be taken from wires connected to pips + // They are good representation for nodes + std::set wire_names; + for (auto tile_type : ar_.getTileTypeList()) { + auto wires = tile_type.getWires(); + for (auto pip : tile_type.getPips()) { + wire_names.insert(wires[pip.getWire0()]); + wire_names.insert(wires[pip.getWire1()]); + } + } + int num_seg = wire_names.size(); + arch_->Segments.resize(num_seg); + uint32_t index = 0; + for (auto i : wire_names) { + // Use default values as we will populate rr_graph with correct values + // This segments are just declaration of future use + arch_->Segments[index].name = 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].parallel_axis = BOTH_AXIS; + arch_->Segments[index].directionality = BI_DIRECTIONAL; + arch_->Segments[index].arch_wire_switch = 1; + arch_->Segments[index].arch_opin_switch = 1; + arch_->Segments[index].cb.resize(1); + arch_->Segments[index].cb[0] = true; + arch_->Segments[index].sb.resize(2); + arch_->Segments[index].sb[0] = true; + arch_->Segments[index].sb[1] = true; + ++index; + } + } }; void FPGAInterchangeReadArch(const char* FPGAInterchangeDeviceFile, 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_hash.h b/libs/libvtrutil/src/vtr_hash.h index 99fd55807ac..7e8e6fa42d7 100644 --- a/libs/libvtrutil/src/vtr_hash.h +++ b/libs/libvtrutil/src/vtr_hash.h @@ -15,6 +15,16 @@ inline void hash_combine(std::size_t& seed, const T& v) { seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } +struct hash_pair { + template + std::size_t operator()(const std::pair& pair) const noexcept { + auto hash1 = std::hash{}(pair.first); + auto hash2 = std::hash{}(pair.second); + + return hash1 ^ hash2; + } +}; + } // 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/atom_netlist_utils.cpp b/vpr/src/base/atom_netlist_utils.cpp index 9f73cb84426..cbfed079c25 100644 --- a/vpr/src/base/atom_netlist_utils.cpp +++ b/vpr/src/base/atom_netlist_utils.cpp @@ -95,7 +95,10 @@ void print_netlist_as_blif(FILE* f, const AtomNetlist& netlist) { AtomPinId pin = *netlist.block_pins(blk_id).begin(); std::string blk_name = netlist.block_name(blk_id); - std::string out_name(blk_name.begin() + 4, blk_name.end()); //+4 to trim out: prefix + + std::string out_prefix("out:"); + int strip_size = blk_name.substr(0, out_prefix.size()) == out_prefix ? out_prefix.size() : 0; + std::string out_name(blk_name.begin() + strip_size, blk_name.end()); //+4 to trim out: prefix if present fprintf(f, "%s%s", INDENT, out_name.c_str()); @@ -307,7 +310,7 @@ void print_netlist_as_blif(FILE* f, const AtomNetlist& netlist) { ports.push_back(port_id); } - fprintf(f, ".subckt %s \\\n", blk_model->name); + fprintf(f, ".subckt %s \\\n", netlist.block_name(blk_id).c_str()); for (size_t i = 0; i < ports.size(); i++) { auto width = netlist.port_width(ports[i]); for (size_t j = 0; j < width; ++j) { diff --git a/vpr/src/base/read_circuit.cpp b/vpr/src/base/read_circuit.cpp index cee5fbe9e9a..9bc171fe2ad 100644 --- a/vpr/src/base/read_circuit.cpp +++ b/vpr/src/base/read_circuit.cpp @@ -1,5 +1,6 @@ #include "read_circuit.h" #include "read_blif.h" +#include "read_interchange_netlist.h" #include "atom_netlist.h" #include "atom_netlist_utils.h" #include "echo_files.h" @@ -21,20 +22,23 @@ static void process_circuit(AtomNetlist& netlist, static void show_circuit_stats(const AtomNetlist& netlist); -AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, - const char* circuit_file, - const t_model* user_models, - const t_model* library_models, - e_const_gen_inference const_gen_inference, - bool should_absorb_buffers, - bool should_sweep_dangling_primary_ios, - bool should_sweep_dangling_nets, - bool should_sweep_dangling_blocks, - bool should_sweep_constant_primary_outputs, - int verbosity) { +AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, t_vpr_setup& vpr_setup, t_arch& arch) { + // Options + const char* circuit_file = vpr_setup.PackerOpts.circuit_file_name.c_str(); + 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; + bool should_absorb_buffers = vpr_setup.NetlistOpts.absorb_buffer_luts; + bool should_sweep_dangling_primary_ios = vpr_setup.NetlistOpts.sweep_dangling_primary_ios; + bool should_sweep_dangling_nets = vpr_setup.NetlistOpts.sweep_dangling_nets; + bool should_sweep_dangling_blocks = vpr_setup.NetlistOpts.sweep_dangling_blocks; + bool should_sweep_constant_primary_outputs = vpr_setup.NetlistOpts.sweep_constant_primary_outputs; + bool verbosity = vpr_setup.NetlistOpts.netlist_verbosity; + if (circuit_format == e_circuit_format::AUTO) { auto name_ext = vtr::split_ext(circuit_file); + VTR_LOG("%s\n", circuit_file); if (name_ext[1] == ".blif") { circuit_format = e_circuit_format::BLIF; } else if (name_ext[1] == ".eblif") { @@ -49,10 +53,18 @@ AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, { vtr::ScopedStartFinishTimer t("Load circuit"); - VTR_ASSERT(circuit_format == e_circuit_format::BLIF - || circuit_format == e_circuit_format::EBLIF); - - netlist = read_blif(circuit_format, circuit_file, user_models, library_models); + switch (circuit_format) { + case e_circuit_format::BLIF: + case e_circuit_format::EBLIF: + netlist = read_blif(circuit_format, circuit_file, user_models, library_models); + break; + case e_circuit_format::FPGA_INTERCHANGE: + netlist = read_interchange_netlist(circuit_file, arch); + break; + default: + VTR_ASSERT(false); + break; + } } if (isEchoFileEnabled(E_ECHO_ATOM_NETLIST_ORIG)) { diff --git a/vpr/src/base/read_circuit.h b/vpr/src/base/read_circuit.h index a7935aa99e4..90be01a3891 100644 --- a/vpr/src/base/read_circuit.h +++ b/vpr/src/base/read_circuit.h @@ -5,20 +5,11 @@ #include "vpr_types.h" enum class e_circuit_format { - AUTO, /// +#include +#include +#include +#include +#include +#include +#include +#include + +#include "LogicalNetlist.capnp.h" +#include "capnp/serialize.h" +#include "capnp/serialize-packed.h" + +#include "atom_netlist.h" + +#include "vtr_assert.h" +#include "vtr_hash.h" +#include "vtr_util.h" +#include "vtr_log.h" +#include "vtr_logic.h" +#include "vtr_time.h" +#include "vtr_digest.h" + +#include "vpr_types.h" +#include "vpr_error.h" +#include "globals.h" +#include "read_interchange_netlist.h" +#include "arch_types.h" + +struct NetlistReader { + public: + NetlistReader(AtomNetlist& main_netlist, + LogicalNetlist::Netlist::Reader& netlist_reader, + const std::string netlist_id, + const char* netlist_file, + const t_arch& arch) + : main_netlist_(main_netlist) + , nr_(netlist_reader) + , netlist_file_(netlist_file) + , arch_(arch) { + // Define top module + top_cell_instance_ = nr_.getTopInst(); + + auto str_list = nr_.getStrList(); + main_netlist_ = AtomNetlist(str_list[top_cell_instance_.getName()], netlist_id); + + inpad_model_ = find_model(MODEL_INPUT); + outpad_model_ = find_model(MODEL_OUTPUT); + main_netlist_.set_block_types(inpad_model_, outpad_model_); + + VTR_LOG("Reading IOs...\n"); + read_ios(); + VTR_LOG("Reading names...\n"); + read_names(); + VTR_LOG("Reading blocks...\n"); + read_blocks(); + } + + private: + AtomNetlist& main_netlist_; + // Netlist Reader + LogicalNetlist::Netlist::Reader& nr_; + + const char* netlist_file_; + + const t_model* inpad_model_; + const t_model* outpad_model_; + const t_arch& arch_; + + LogicalNetlist::Netlist::CellInstance::Reader top_cell_instance_; + + void read_ios() { + const t_model* input_model = find_model(MODEL_INPUT); + const t_model* output_model = find_model(MODEL_OUTPUT); + + auto str_list = nr_.getStrList(); + + auto top_cell_decl = nr_.getCellDecls()[top_cell_instance_.getCell()]; + for (auto top_port : top_cell_decl.getPorts()) { + auto port = nr_.getPortList()[top_port]; + auto name = std::string(str_list[port.getName()].cStr()); + auto dir = port.getDir(); + + int bus_size, start_bit; + std::tie(bus_size, start_bit) = get_bus_size(port); + + for (int bit = start_bit; bit < start_bit + bus_size; bit++) { + auto port_name = name; + if (bus_size > 1) + port_name = name + "[" + std::to_string(bit) + "]"; + + AtomBlockId blk_id; + AtomPortId port_id; + AtomNetId net_id; + + switch (dir) { + case LogicalNetlist::Netlist::Direction::INPUT: + blk_id = main_netlist_.create_block(port_name, input_model); + port_id = main_netlist_.create_port(blk_id, input_model->outputs); + net_id = main_netlist_.create_net(port_name); + main_netlist_.create_pin(port_id, 0, net_id, PinType::DRIVER); + break; + case LogicalNetlist::Netlist::Direction::OUTPUT: + blk_id = main_netlist_.create_block(port_name, output_model); + port_id = main_netlist_.create_port(blk_id, output_model->inputs); + net_id = main_netlist_.create_net(port_name); + main_netlist_.create_pin(port_id, 0, net_id, PinType::SINK); + break; + default: + VTR_ASSERT(0); + break; + } + } + } + } + + void read_names() { + const t_model* blk_model = find_model(MODEL_NAMES); + + // Set the max size of the LUT + int lut_size = 0; + for (auto lut : arch_.lut_cells) + lut_size = std::max((int)lut.inputs.size(), lut_size); + blk_model->inputs[0].size = lut_size; + + auto top_cell = nr_.getCellList()[nr_.getTopInst().getCell()]; + auto decl_list = nr_.getCellDecls(); + auto inst_list = nr_.getInstList(); + auto port_list = nr_.getPortList(); + auto str_list = nr_.getStrList(); + + std::vector> insts; + for (auto cell_inst : top_cell.getInsts()) { + auto cell = decl_list[inst_list[cell_inst].getCell()]; + + bool is_lut; + int width; + std::string init_param; + std::tie(is_lut, width, init_param) = is_lut_cell(str_list[cell.getName()].cStr()); + + if (is_lut) + insts.emplace_back(cell_inst, width, init_param); + } + + for (auto inst : insts) { + unsigned int inst_idx; + int lut_width; + std::string init_param; + std::tie(inst_idx, lut_width, init_param) = inst; + + std::string inst_name = str_list[inst_list[inst_idx].getName()].cStr(); + + auto props = inst_list[inst_idx].getPropMap().getEntries(); + std::vector init; + for (auto entry : props) { + if (std::string(str_list[entry.getKey()].cStr()) != 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 bit_regex("[0-1]+"); + std::string init_str = std::string(str_list[entry.getTextValue()].cStr()); + std::smatch regex_matches; + + // Fill the init vector + if (std::regex_match(init_str, regex_matches, vhex_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, bit_regex)) + for (const char& c : init_str) + init.push_back((bool)std::stoi(std::string(1, c), 0, 2)); + } + } + + // Add proper LUT mapping function here based on LUT size and init value + AtomNetlist::TruthTable truth_table; + bool is_const = false; + for (int bit = 0; bit < (int)init.size(); bit++) { + bool bit_set = init[init.size() - bit - 1]; + + if (bit_set == 0) + continue; + + is_const = bit == 0; + + truth_table.emplace_back(); + for (int row_bit = lut_width - 1; row_bit >= 0; row_bit--) { + bool row_bit_set = (bit >> row_bit) & 1; + auto log_value = row_bit_set ? vtr::LogicValue::TRUE : vtr::LogicValue::FALSE; + + truth_table[truth_table.size() - 1].push_back(log_value); + } + truth_table[truth_table.size() - 1].push_back(vtr::LogicValue::TRUE); + } + + //Figure out if the output is a constant generator + bool output_is_const = false; + if (truth_table.empty()) { + //An empty truth table in BLIF corresponds to a constant-zero + // e.g. + // + // #gnd is a constant 0 generator + // .names gnd + // + //An single entry truth table with value '0' also corresponds to a constant-zero + // e.g. + // + // #gnd2 is a constant 0 generator + // .names gnd2 + // 0 + // + output_is_const = true; + VTR_LOG("Found constant-zero generator '%s'\n", inst_name.c_str()); + } else if (truth_table.size() == 1 && is_const) { + //A single-entry truth table with value '1' in BLIF corresponds to a constant-one + // e.g. + // + // #vcc is a constant 1 generator + // .names vcc + // 1 + // + output_is_const = true; + VTR_LOG("Found constant-one generator '%s'\n", inst_name.c_str()); + } + + AtomBlockId blk_id = main_netlist_.create_block(inst_name, blk_model, truth_table); + + AtomPortId iport_id = main_netlist_.create_port(blk_id, blk_model->inputs); + AtomPortId oport_id = main_netlist_.create_port(blk_id, blk_model->outputs); + + auto cell_lib = decl_list[inst_list[inst_idx].getCell()]; + std::unordered_map port_net_map; + + for (auto net : top_cell.getNets()) { + std::string net_name = str_list[net.getName()].cStr(); + for (auto port : net.getPortInsts()) { + if (!port.isInst() || port.getInst() != inst_idx) + continue; + + port_net_map.emplace(port.getPort(), net_name); + } + } + + int inum = 0; + for (auto port : cell_lib.getPorts()) { + auto net_name = port_net_map.at(port); + AtomNetId net_id = main_netlist_.create_net(net_name); + + auto dir = port_list[port].getDir(); + switch (dir) { + case LogicalNetlist::Netlist::Direction::INPUT: + if (!output_is_const) main_netlist_.create_pin(iport_id, inum++, net_id, PinType::SINK); + break; + case LogicalNetlist::Netlist::Direction::OUTPUT: + main_netlist_.create_pin(oport_id, 0, net_id, PinType::DRIVER, output_is_const); + break; + default: + VTR_ASSERT(0); + break; + } + } + } + } + + void read_blocks() { + auto top_cell = nr_.getCellList()[nr_.getTopInst().getCell()]; + auto decl_list = nr_.getCellDecls(); + auto inst_list = nr_.getInstList(); + auto port_list = nr_.getPortList(); + auto str_list = nr_.getStrList(); + + std::vector> insts; + for (auto cell_inst : top_cell.getInsts()) { + 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()); + + if (!is_lut) + insts.emplace_back(cell_inst, inst_list[cell_inst].getCell()); + } + + for (auto inst_pair : insts) { + auto inst_idx = inst_pair.first; + auto cell_idx = inst_pair.second; + + auto model_name = str_list[decl_list[cell_idx].getName()].cStr(); + const t_model* blk_model = find_model(model_name); + + std::string inst_name = str_list[inst_list[inst_idx].getName()].cStr(); + VTR_ASSERT(inst_name.empty() == 0); + + //The name for every block should be unique, check that there is no name conflict + AtomBlockId blk_id = main_netlist_.find_block(inst_name); + if (blk_id) { + const t_model* conflicting_model = main_netlist_.block_model(blk_id); + vpr_throw(VPR_ERROR_IC_NETLIST_F, netlist_file_, -1, + "Duplicate blocks named '%s' found in netlist." + " Existing block of type '%s' conflicts with subckt of type '%s'.", + inst_name.c_str(), conflicting_model->name, blk_model->name); + } + + 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) + inst_name = arch_.vcc_cell; + else if (std::string(str_list[cell.getName()].cStr()) == arch_.gnd_cell) + inst_name = arch_.gnd_cell; + + if (main_netlist_.find_block(inst_name)) + continue; + + //Create the block + blk_id = main_netlist_.create_block(inst_name, blk_model); + + std::unordered_set added_ports; + for (auto port_net : port_net_map) { + auto port_idx = port_net.first.first; + auto port_bit = port_net.first.second; + + auto net_name = port_net.second; + if (inst_name == arch_.vcc_cell) + net_name = arch_.vcc_net; + else if (inst_name == arch_.gnd_cell) + net_name = arch_.gnd_net; + + auto port = port_list[port_idx]; + auto port_name = str_list[port.getName()].cStr(); + + //Check for consistency between model and ports + const t_model_ports* model_port = find_model_port(blk_model, std::string(port_name)); + VTR_ASSERT(model_port); + + //Determine the pin type + PinType pin_type = PinType::SINK; + if (model_port->dir == OUT_PORT) { + pin_type = PinType::DRIVER; + } else { + VTR_ASSERT_MSG(model_port->dir == IN_PORT, "Unexpected port type"); + } + + AtomPortId port_id = main_netlist_.create_port(blk_id, model_port); + + //Make the net + AtomNetId net_id = main_netlist_.create_net(std::string(net_name)); + + //Make the pin + main_netlist_.create_pin(port_id, port_bit, net_id, pin_type); + + added_ports.emplace(port_id); + } + + // Bind unconnected ports to VCC by default + for (const t_model_ports* ports : {blk_model->inputs, blk_model->outputs}) { + for (const t_model_ports* port = ports; port != nullptr; port = port->next) { + AtomPortId port_id = main_netlist_.create_port(blk_id, port); + + if (added_ports.count(port_id)) + continue; + + if (port->dir != IN_PORT) + continue; + + //Make the net + AtomNetId net_id = main_netlist_.create_net(arch_.vcc_net); + + PinType pin_type = PinType::SINK; + //Make the pin + for (int i = 0; i < port->size; i++) + main_netlist_.create_pin(port_id, i, net_id, pin_type); + } + } + } + } + + // + // Utilities + // + const t_model* find_model(std::string name) { + for (const auto models : {arch_.models, arch_.model_library}) + for (const t_model* model = models; model != nullptr; model = model->next) + if (name == model->name) + return model; + + vpr_throw(VPR_ERROR_IC_NETLIST_F, netlist_file_, -1, "Failed to find matching architecture model for '%s'\n", name.c_str()); + } + + const t_model_ports* find_model_port(const t_model* blk_model, std::string name) { + //We now look through all the ports on the model looking for the matching port + for (const t_model_ports* ports : {blk_model->inputs, blk_model->outputs}) + for (const t_model_ports* port = ports; port != nullptr; port = port->next) + if (name == std::string(port->name)) + return port; + + //No match + vpr_throw(VPR_ERROR_IC_NETLIST_F, netlist_file_, -1, + "Found no matching port '%s' on architecture model '%s'\n", + name.c_str(), blk_model->name); + return nullptr; + } + + std::pair get_bus_size(LogicalNetlist::Netlist::Port::Reader port_reader) { + if (port_reader.isBus()) { + int s = port_reader.getBus().getBusStart(); + int e = port_reader.getBus().getBusEnd(); + + if (e < s) + return std::make_pair(s - e + 1, e); + else + return std::make_pair(e - s + 1, s); + } + + return std::make_pair(1, 0); + } + + unsigned int get_port_bit(LogicalNetlist::Netlist::PortInstance::Reader port_inst_reader) { + if (port_inst_reader.getBusIdx().which() == LogicalNetlist::Netlist::PortInstance::BusIdx::IDX) + return port_inst_reader.getBusIdx().getIdx(); + + return 0; + } + + std::unordered_map, std::string, vtr::hash_pair> get_port_net_map(unsigned int inst_idx) { + auto top_cell = nr_.getCellList()[nr_.getTopInst().getCell()]; + 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(); + for (auto port : net.getPortInsts()) { + if (!port.isInst() || port.getInst() != inst_idx) + continue; + + unsigned int port_bit = get_port_bit(port); + auto pair = std::make_pair(port.getPort(), port_bit); + map.emplace(pair, net_name); + } + } + + return map; + } + + std::tuple is_lut_cell(std::string cell_name) { + for (auto lut_cell : arch_.lut_cells) { + if (cell_name == lut_cell.name) { + auto init_param = lut_cell.init_param; + + // Assign default value for the LUT init parameter, if inexistent + if (init_param.empty()) + init_param = "INIT"; + + return std::make_tuple(true, lut_cell.inputs.size(), init_param); + } + } + + return std::make_tuple(false, 0, ""); + } +}; + +AtomNetlist read_interchange_netlist(const char* ic_netlist_file, + t_arch& arch) { + AtomNetlist netlist; + std::string netlist_id = vtr::secure_digest_file(ic_netlist_file); + + // Decompress GZipped capnproto device file + gzFile file = gzopen(ic_netlist_file, "r"); + VTR_ASSERT(file != Z_NULL); + + std::vector output_data; + output_data.resize(4096); + std::stringstream sstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary); + while (true) { + int ret = gzread(file, output_data.data(), output_data.size()); + VTR_ASSERT(ret >= 0); + if (ret > 0) { + sstream.write((const char*)output_data.data(), ret); + VTR_ASSERT(sstream); + } else { + VTR_ASSERT(ret == 0); + int error; + gzerror(file, &error); + VTR_ASSERT(error == Z_OK); + break; + } + } + + VTR_ASSERT(gzclose(file) == Z_OK); + + sstream.seekg(0); + kj::std::StdInputStream istream(sstream); + + // Reader options + capnp::ReaderOptions reader_options; + reader_options.nestingLimit = std::numeric_limits::max(); + reader_options.traversalLimitInWords = std::numeric_limits::max(); + + capnp::InputStreamMessageReader message_reader(istream, reader_options); + + auto netlist_reader = message_reader.getRoot(); + + NetlistReader reader(netlist, netlist_reader, netlist_id, ic_netlist_file, arch); + + return netlist; +} diff --git a/vpr/src/base/read_interchange_netlist.h b/vpr/src/base/read_interchange_netlist.h new file mode 100644 index 00000000000..1b17b86ea5d --- /dev/null +++ b/vpr/src/base/read_interchange_netlist.h @@ -0,0 +1,10 @@ +#ifndef READ_INTERCHANGE_NETLIST_H +#define READ_INTERCHANGE_NETLIST_H +#include "logic_types.h" +#include "atom_netlist_fwd.h" +#include "read_circuit.h" + +AtomNetlist read_interchange_netlist(const char* ic_netlist_file, + t_arch& arch); + +#endif /*READ_INTERCHANGE_NETLIST_H*/ diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index f6726d6689d..f4087b64c82 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -101,6 +101,8 @@ struct ParseCircuitFormat { conv_value.set_value(e_circuit_format::BLIF); else if (str == "eblif") conv_value.set_value(e_circuit_format::EBLIF); + else if (str == "fpga-interchange") + conv_value.set_value(e_circuit_format::FPGA_INTERCHANGE); else { std::stringstream msg; msg << "Invalid conversion from '" << str << "' to e_circuit_format (expected one of: " << argparse::join(default_choices(), ", ") << ")"; @@ -116,16 +118,18 @@ struct ParseCircuitFormat { conv_value.set_value("auto"); else if (val == e_circuit_format::BLIF) conv_value.set_value("blif"); - else { - VTR_ASSERT(val == e_circuit_format::EBLIF); + else if (val == e_circuit_format::EBLIF) conv_value.set_value("eblif"); + else { + VTR_ASSERT(val == e_circuit_format::FPGA_INTERCHANGE); + conv_value.set_value("fpga-interchange"); } return conv_value; } std::vector default_choices() { - return {"auto", "blif", "eblif"}; + return {"auto", "blif", "eblif", "fpga-interchange"}; } }; struct ParseRoutePredictor { @@ -1423,7 +1427,8 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg " .conn - Connection between two wires\n" " .cname - Custom name for atom primitive\n" " .param - Parameter on atom primitive\n" - " .attr - Attribute on atom primitive\n") + " .attr - Attribute on atom primitive\n" + " * fpga-interchange: Logical netlist in FPGA Interchange schema format\n") .default_value("auto") .show_in(argparse::ShowIn::HELP_ONLY); diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index e52f812c437..3b3b8dbe15e 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -313,17 +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->PackerOpts.circuit_file_name.c_str(), - vpr_setup->user_models, - vpr_setup->library_models, - vpr_setup->NetlistOpts.const_gen_inference, - vpr_setup->NetlistOpts.absorb_buffer_luts, - vpr_setup->NetlistOpts.sweep_dangling_primary_ios, - vpr_setup->NetlistOpts.sweep_dangling_nets, - vpr_setup->NetlistOpts.sweep_dangling_blocks, - vpr_setup->NetlistOpts.sweep_constant_primary_outputs, - vpr_setup->NetlistOpts.netlist_verbosity); + atom_ctx.nlist = read_and_process_circuit(options->circuit_format, *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/util/vpr_error.h b/vpr/src/util/vpr_error.h index 5820ebc0aae..d293a03b94f 100644 --- a/vpr/src/util/vpr_error.h +++ b/vpr/src/util/vpr_error.h @@ -19,6 +19,7 @@ enum e_vpr_error { VPR_ERROR_NET_F, VPR_ERROR_PLACE_F, VPR_ERROR_BLIF_F, + VPR_ERROR_IC_NETLIST_F, VPR_ERROR_IMPL_NETLIST_WRITER, VPR_ERROR_NETLIST, VPR_ERROR_ATOM_NETLIST, diff --git a/vpr/test/lut.netlist b/vpr/test/lut.netlist new file mode 100644 index 00000000000..7f2833b66d1 Binary files /dev/null and b/vpr/test/lut.netlist differ diff --git a/vpr/test/test_interchange_device.cpp b/vpr/test/test_interchange_device.cpp index 268e6135085..2d6bd4ae64f 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) { @@ -41,4 +41,145 @@ TEST_CASE("read_interchange_models", "[vpr]") { REQUIRE(lib_models.size() == 0); } +TEST_CASE("read_interchange_layout", "[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); + + auto& gd = arch.grid_layouts[0]; + REQUIRE(gd.grid_type == GridDefType::FIXED); + REQUIRE(gd.height == 10); + REQUIRE(gd.width == 10); + + std::unordered_map tile_types({{"NULL", false}, {"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()); + REQUIRE(loc.priority == 1); + + ret->second = true; + } + + for (auto type : tile_types) { + CHECK(type.second); + } +} + +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); + REQUIRE(arch.lut_bels.size() == 2); + + 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 (auto lut_bel : arch.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 new file mode 100644 index 00000000000..2af1b33a96f --- /dev/null +++ b/vpr/test/test_interchange_netlist.cpp @@ -0,0 +1,33 @@ +#include "catch2/catch_test_macros.hpp" + +#include "read_circuit.h" +#include "read_fpga_interchange_arch.h" +#include "arch_util.h" +#include "vpr_api.h" +#include +#include +#include + +namespace { + +static constexpr const char kArchFile[] = "testarch.device"; + +TEST_CASE("read_interchange_netlist", "[vpr]") { + t_arch arch; + t_vpr_setup vpr_setup; + + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + vpr_setup.user_models = arch.models; + vpr_setup.library_models = arch.model_library; + vpr_setup.PackerOpts.circuit_file_name = "lut.netlist"; + + /* 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); +} + +} // namespace diff --git a/vpr/test/testarch.device b/vpr/test/testarch.device index e9b12b1ac89..59cb916bfb0 100644 Binary files a/vpr/test/testarch.device and b/vpr/test/testarch.device differ