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