/* The XML parser processes an XML file into a tree data structure composed of * pugi::xml_nodes. Each node represents an XML element. For example * will generate two pugi::xml_nodes. One called "a" and its * child "b". Each pugi::xml_node can contain various XML data such as attribute * information and text content. The XML parser provides several functions to * help the developer build, and traverse tree (this is also somtime referred to * as the Document Object Model or DOM). * * For convenience it often makes sense to use some wraper functions (provided in * the pugiutil namespace of libvtrutil) which simplify loading an XML file and * error handling. * * The function pugiutil::load_xml() reads in an xml file. * * The function pugiutil::get_single_child() returns a child xml_node for a given parent * xml_node if there is a child which matches the name provided by the developer. * * The function pugiutil::get_attribute() is used to extract attributes from an * xml_node, returning a pugi::xml_attribute. xml_attribute objects support accessors * such as as_float(), as_int() to retrieve semantic values. See pugixml documentation * for more details. * * Architecture file checks already implemented (Daniel Chen): * - Duplicate pb_types, pb_type ports, models, model ports, * interconnects, interconnect annotations. * - Port and pin range checking (port with 4 pins can only be * accessed within [0:3]. * - LUT delay matrix size matches # of LUT inputs * - Ensures XML tags are ordered. * - Clocked primitives that have timing annotations must have a clock * name matching the primitive. * - Enforced VPR definition of LUT and FF must have one input port (n pins) * and one output port(1 pin). * - Checks file extension for blif and architecture xml file, avoid crashes if * the two files are swapped on command line. * */ #include #include #include #include #include #include #include "pugixml.hpp" #include "pugixml_util.hpp" #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_util.h" #include "vtr_memory.h" #include "vtr_digest.h" #include "vtr_token.h" #include "vtr_bimap.h" #include "arch_types.h" #include "arch_util.h" #include "arch_error.h" #include "read_xml_arch_file.h" #include "read_xml_util.h" #include "parse_switchblocks.h" using namespace std::string_literals; using pugiutil::ReqOpt; struct t_fc_override { std::string port_name; std::string seg_name; e_fc_value_type fc_value_type; float fc_value; }; /* This gives access to the architecture file name to * all architecture-parser functions */ static const char* arch_file_name = nullptr; /* Function prototypes */ /* Populate data */ static void SetupPinLocationsAndPinClasses(pugi::xml_node Locations, t_physical_tile_type* PhysicalTileType, const pugiutil::loc_data& loc_data); static void LoadPinLoc(pugi::xml_node Locations, t_physical_tile_type* type, const pugiutil::loc_data& loc_data); template static std::pair ProcessPinString(pugi::xml_node Locations, T type, const char* pin_loc_string, const pugiutil::loc_data& loc_data); /* Process XML hierarchy */ static void ProcessTiles(pugi::xml_node Node, std::vector& PhysicalTileTypes, std::vector& LogicalBlockTypes, const t_default_fc_spec& arch_def_fc, t_arch& arch, const pugiutil::loc_data& loc_data); static void ProcessTileProps(pugi::xml_node Node, t_physical_tile_type* PhysicalTileType, const pugiutil::loc_data& loc_data); static void ProcessTilePorts(pugi::xml_node Parent, t_physical_tile_type* PhysicalTileType, const pugiutil::loc_data& loc_data); static void ProcessTilePort(pugi::xml_node Node, t_physical_tile_port* port, const pugiutil::loc_data& loc_data); static void ProcessTileEquivalentSites(pugi::xml_node Parent, t_physical_tile_type* PhysicalTileType, std::vector& LogicalBlockTypes, const pugiutil::loc_data& loc_data); static void ProcessEquivalentSiteDirectConnection(pugi::xml_node Parent, t_physical_tile_type* PhysicalTileType, t_logical_block_type* LogicalBlockType, const pugiutil::loc_data& loc_data); static void ProcessEquivalentSiteCustomConnection(pugi::xml_node Parent, t_physical_tile_type* PhysicalTileType, t_logical_block_type* LogicalBlockType, std::string site_name, const pugiutil::loc_data& loc_data); static void ProcessPb_Type(pugi::xml_node Parent, t_pb_type* pb_type, t_mode* mode, const bool timing_enabled, const t_arch& arch, const pugiutil::loc_data& loc_data); static void ProcessPb_TypePort(pugi::xml_node Parent, t_port* port, e_power_estimation_method power_method, const bool is_root_pb_type, const pugiutil::loc_data& loc_data); static void ProcessPinToPinAnnotations(pugi::xml_node parent, t_pin_to_pin_annotation* annotation, t_pb_type* parent_pb_type, const pugiutil::loc_data& loc_data); static void ProcessInterconnect(pugi::xml_node Parent, t_mode* mode, const pugiutil::loc_data& loc_data); static void ProcessMode(pugi::xml_node Parent, t_mode* mode, const bool timing_enabled, const t_arch& arch, const pugiutil::loc_data& loc_data); static t_metadata_dict ProcessMetadata(pugi::xml_node Parent, const pugiutil::loc_data& loc_data); static void Process_Fc_Values(pugi::xml_node Node, t_default_fc_spec& spec, const pugiutil::loc_data& loc_data); static void Process_Fc(pugi::xml_node Node, t_physical_tile_type* PhysicalTileType, std::vector& segments, const t_default_fc_spec& arch_def_fc, const pugiutil::loc_data& loc_data); static t_fc_override Process_Fc_override(pugi::xml_node node, const pugiutil::loc_data& loc_data); static void ProcessSwitchblockLocations(pugi::xml_node switchblock_locations, t_physical_tile_type* type, const t_arch& arch, const pugiutil::loc_data& loc_data); static e_fc_value_type string_to_fc_value_type(const std::string& str, pugi::xml_node node, const pugiutil::loc_data& loc_data); static void ProcessChanWidthDistr(pugi::xml_node Node, t_arch* arch, const pugiutil::loc_data& loc_data); static void ProcessChanWidthDistrDir(pugi::xml_node Node, t_chan* chan, const pugiutil::loc_data& loc_data); static void ProcessModels(pugi::xml_node Node, t_arch* arch, const pugiutil::loc_data& loc_data); static void ProcessModelPorts(pugi::xml_node port_group, t_model* model, std::set& port_names, const pugiutil::loc_data& loc_data); static void ProcessLayout(pugi::xml_node Node, t_arch* arch, const pugiutil::loc_data& loc_data); static t_grid_def ProcessGridLayout(pugi::xml_node layout_type_tag, const pugiutil::loc_data& loc_data); static void ProcessDevice(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& arch_def_fc, const pugiutil::loc_data& loc_data); static void ProcessComplexBlocks(pugi::xml_node Node, std::vector& LogicalBlockTypes, t_arch& arch, const bool timing_enabled, const pugiutil::loc_data& loc_data); static void ProcessSwitches(pugi::xml_node Node, t_arch_switch_inf** Switches, int* NumSwitches, const bool timing_enabled, const pugiutil::loc_data& loc_data); static void ProcessSwitchTdel(pugi::xml_node Node, const bool timing_enabled, const int switch_index, t_arch_switch_inf* Switches, const pugiutil::loc_data& loc_data); static void ProcessDirects(pugi::xml_node Parent, t_direct_inf** Directs, int* NumDirects, const t_arch_switch_inf* Switches, const int NumSwitches, const pugiutil::loc_data& loc_data); static void ProcessClockMetalLayers(pugi::xml_node parent, std::unordered_map& metal_layers, pugiutil::loc_data& loc_data); static void ProcessClockNetworks(pugi::xml_node parent, std::vector& clock_networks, const t_arch_switch_inf* switches, const int num_switches, pugiutil::loc_data& loc_data); static void ProcessClockSwitchPoints(pugi::xml_node parent, t_clock_network_arch& clock_network, const t_arch_switch_inf* switches, const int num_switches, pugiutil::loc_data& loc_data); static void ProcessClockRouting(pugi::xml_node parent, std::vector& clock_connections, const t_arch_switch_inf* switches, const int num_switches, pugiutil::loc_data& loc_data); static void ProcessSegments(pugi::xml_node Parent, std::vector& Segs, const t_arch_switch_inf* Switches, const int NumSwitches, const bool timing_enabled, const bool switchblocklist_required, const pugiutil::loc_data& loc_data); static void ProcessSwitchblocks(pugi::xml_node Parent, t_arch* arch, const pugiutil::loc_data& loc_data); static void ProcessCB_SB(pugi::xml_node Node, std::vector& list, const pugiutil::loc_data& loc_data); static void ProcessPower(pugi::xml_node parent, t_power_arch* power_arch, const pugiutil::loc_data& loc_data); static void ProcessClocks(pugi::xml_node Parent, t_clock_arch* clocks, const pugiutil::loc_data& loc_data); static void ProcessPb_TypePowerEstMethod(pugi::xml_node Parent, t_pb_type* pb_type, const pugiutil::loc_data& loc_data); static void ProcessPb_TypePort_Power(pugi::xml_node Parent, t_port* port, e_power_estimation_method power_method, const pugiutil::loc_data& loc_data); bool check_model_combinational_sinks(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model); void warn_model_missing_timing(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model); bool check_model_clocks(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model); bool check_leaf_pb_model_timing_consistency(const t_pb_type* pb_type, const t_arch& arch); 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); std::string inst_port_to_port_name(std::string inst_port); static bool attribute_to_bool(const pugi::xml_node node, const pugi::xml_attribute attr, const pugiutil::loc_data& loc_data); 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); static void check_port_direct_mappings(t_physical_tile_type_ptr physical_tile, t_logical_block_type_ptr logical_block); static const t_physical_tile_port* get_port_by_name(t_physical_tile_type_ptr type, const char* port_name); static const t_port* get_port_by_name(t_logical_block_type_ptr type, const char* port_name); static const t_physical_tile_port* get_port_by_pin(t_physical_tile_type_ptr type, int pin); static const t_port* get_port_by_pin(t_logical_block_type_ptr type, int pin); template static T* get_type_by_name(const char* type_name, std::vector& types); /* * * * External Function Implementations * * */ /* Loads the given architecture file. */ void XmlReadArch(const char* ArchFile, const bool timing_enabled, t_arch* arch, std::vector& PhysicalTileTypes, std::vector& LogicalBlockTypes) { pugi::xml_node Next; ReqOpt POWER_REQD, SWITCHBLOCKLIST_REQD; if (vtr::check_file_name_extension(ArchFile, ".xml") == false) { VTR_LOG_WARN( "Architecture file '%s' may be in incorrect format. " "Expecting .xml format for architecture files.\n", ArchFile); } //Create a unique identifier for this architecture file based on it's contents arch->architecture_id = vtr::strdup(vtr::secure_digest_file(ArchFile).c_str()); /* Parse the file */ pugi::xml_document doc; pugiutil::loc_data loc_data; t_default_fc_spec arch_def_fc; try { loc_data = pugiutil::load_xml(doc, ArchFile); arch_file_name = ArchFile; /* Root node should be architecture */ auto architecture = get_single_child(doc, "architecture", loc_data); /* TODO: do version processing properly with string delimiting on the . */ #if 0 char* Prop = get_attribute(architecture, "version", loc_data, ReqOpt::OPTIONAL).as_string(NULL); if (Prop != NULL) { if (atof(Prop) > atof(VPR_VERSION)) { VTR_LOG_WARN( "This architecture version is for VPR %f while your current VPR version is " VPR_VERSION ", compatability issues may arise\n", atof(Prop)); } } #endif /* Process models */ Next = get_single_child(architecture, "models", loc_data); ProcessModels(Next, arch, loc_data); CreateModelLibrary(arch); /* Process layout */ Next = get_single_child(architecture, "layout", loc_data); ProcessLayout(Next, arch, loc_data); /* Process device */ Next = get_single_child(architecture, "device", loc_data); ProcessDevice(Next, arch, arch_def_fc, loc_data); /* Process switches */ Next = get_single_child(architecture, "switchlist", loc_data); ProcessSwitches(Next, &(arch->Switches), &(arch->num_switches), timing_enabled, loc_data); /* Process switchblocks. This depends on switches */ bool switchblocklist_required = (arch->SBType == CUSTOM); //require this section only if custom switchblocks are used SWITCHBLOCKLIST_REQD = BoolToReqOpt(switchblocklist_required); /* Process segments. This depends on switches */ Next = get_single_child(architecture, "segmentlist", loc_data); ProcessSegments(Next, arch->Segments, arch->Switches, arch->num_switches, timing_enabled, switchblocklist_required, loc_data); Next = get_single_child(architecture, "switchblocklist", loc_data, SWITCHBLOCKLIST_REQD); if (Next) { ProcessSwitchblocks(Next, arch, loc_data); } /* Process logical block types */ Next = get_single_child(architecture, "complexblocklist", loc_data); ProcessComplexBlocks(Next, LogicalBlockTypes, *arch, timing_enabled, loc_data); /* Process logical block types */ Next = get_single_child(architecture, "tiles", loc_data); ProcessTiles(Next, PhysicalTileTypes, LogicalBlockTypes, arch_def_fc, *arch, loc_data); /* Link Physical Tiles with Logical Blocks */ link_physical_logical_types(PhysicalTileTypes, LogicalBlockTypes); /* Process directs */ Next = get_single_child(architecture, "directlist", loc_data, ReqOpt::OPTIONAL); if (Next) { ProcessDirects(Next, &(arch->Directs), &(arch->num_directs), arch->Switches, arch->num_switches, loc_data); } /* Process Clock Networks */ Next = get_single_child(architecture, "clocknetworks", loc_data, ReqOpt::OPTIONAL); if (Next) { std::vector expected_children = {"metal_layers", "clock_network", "clock_routing"}; expect_only_children(Next, expected_children, loc_data); ProcessClockMetalLayers(Next, arch->clock_arch.clock_metal_layers, loc_data); ProcessClockNetworks(Next, arch->clock_arch.clock_networks_arch, arch->Switches, arch->num_switches, loc_data); ProcessClockRouting(Next, arch->clock_arch.clock_connections_arch, arch->Switches, arch->num_switches, loc_data); } /* Process architecture power information */ /* If arch->power has been initialized, meaning the user has requested power estimation, * then the power architecture information is required. */ if (arch->power) { POWER_REQD = ReqOpt::REQUIRED; } else { POWER_REQD = ReqOpt::OPTIONAL; } Next = get_single_child(architecture, "power", loc_data, POWER_REQD); if (Next) { if (arch->power) { ProcessPower(Next, arch->power, loc_data); } else { /* This information still needs to be read, even if it is just * thrown away. */ t_power_arch* power_arch_fake = (t_power_arch*)vtr::calloc(1, sizeof(t_power_arch)); ProcessPower(Next, power_arch_fake, loc_data); free(power_arch_fake); } } // Process Clocks Next = get_single_child(architecture, "clocks", loc_data, POWER_REQD); if (Next) { if (arch->clocks) { ProcessClocks(Next, arch->clocks, loc_data); } else { /* This information still needs to be read, even if it is just * thrown away. */ t_clock_arch* clocks_fake = (t_clock_arch*)vtr::calloc(1, sizeof(t_clock_arch)); ProcessClocks(Next, clocks_fake, loc_data); free(clocks_fake->clock_inf); free(clocks_fake); } } SyncModelsPbTypes(arch, LogicalBlockTypes); UpdateAndCheckModels(arch); } catch (pugiutil::XmlError& e) { archfpga_throw(ArchFile, e.line(), "%s", e.what()); } } /* * * * File-scope function implementations * * */ /* Sets up the pinloc map and pin classes for the type. * Pins and pin classes must already be setup by SetupPinClasses */ static void SetupPinLocationsAndPinClasses(pugi::xml_node Locations, t_physical_tile_type* PhysicalTileType, const pugiutil::loc_data& loc_data) { int i, k, Count; int capacity, pin_count; int num_class; const char* Prop; pugi::xml_node Cur; capacity = PhysicalTileType->capacity; if (!Locations) { PhysicalTileType->pin_location_distribution = E_SPREAD_PIN_DISTR; } else { expect_only_attributes(Locations, {"pattern"}, loc_data); Prop = get_attribute(Locations, "pattern", loc_data).value(); if (strcmp(Prop, "spread") == 0) { PhysicalTileType->pin_location_distribution = E_SPREAD_PIN_DISTR; } else if (strcmp(Prop, "perimeter") == 0) { PhysicalTileType->pin_location_distribution = E_PERIMETER_PIN_DISTR; } else if (strcmp(Prop, "spread_inputs_perimeter_outputs") == 0) { PhysicalTileType->pin_location_distribution = E_SPREAD_INPUTS_PERIMETER_OUTPUTS_PIN_DISTR; } else if (strcmp(Prop, "custom") == 0) { PhysicalTileType->pin_location_distribution = E_CUSTOM_PIN_DISTR; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "%s is an invalid pin location pattern.\n", Prop); } } /* Alloc and clear pin locations */ PhysicalTileType->pinloc = (bool****)vtr::malloc(PhysicalTileType->width * sizeof(int***)); for (int width = 0; width < PhysicalTileType->width; ++width) { PhysicalTileType->pinloc[width] = (bool***)vtr::malloc(PhysicalTileType->height * sizeof(int**)); for (int height = 0; height < PhysicalTileType->height; ++height) { PhysicalTileType->pinloc[width][height] = (bool**)vtr::malloc(4 * sizeof(int*)); for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { PhysicalTileType->pinloc[width][height][side] = (bool*)vtr::malloc(PhysicalTileType->num_pins * sizeof(int)); for (int pin = 0; pin < PhysicalTileType->num_pins; ++pin) { PhysicalTileType->pinloc[width][height][side][pin] = false; } } } } PhysicalTileType->pin_loc_assignments = (char*****)vtr::malloc(PhysicalTileType->width * sizeof(char****)); PhysicalTileType->num_pin_loc_assignments = (int***)vtr::malloc(PhysicalTileType->width * sizeof(int**)); for (int width = 0; width < PhysicalTileType->width; ++width) { PhysicalTileType->pin_loc_assignments[width] = (char****)vtr::calloc(PhysicalTileType->height, sizeof(char***)); PhysicalTileType->num_pin_loc_assignments[width] = (int**)vtr::calloc(PhysicalTileType->height, sizeof(int*)); for (int height = 0; height < PhysicalTileType->height; ++height) { PhysicalTileType->pin_loc_assignments[width][height] = (char***)vtr::calloc(4, sizeof(char**)); PhysicalTileType->num_pin_loc_assignments[width][height] = (int*)vtr::calloc(4, sizeof(int)); } } /* Load the pin locations */ if (PhysicalTileType->pin_location_distribution == E_CUSTOM_PIN_DISTR) { expect_only_children(Locations, {"loc"}, loc_data); Cur = Locations.first_child(); std::set> seen_sides; while (Cur) { check_node(Cur, "loc", loc_data); expect_only_attributes(Cur, {"side", "xoffset", "yoffset"}, loc_data); /* Get offset (ie. height) */ int x_offset = get_attribute(Cur, "xoffset", loc_data, ReqOpt::OPTIONAL).as_int(0); int y_offset = get_attribute(Cur, "yoffset", loc_data, ReqOpt::OPTIONAL).as_int(0); /* Get side */ e_side side = TOP; Prop = get_attribute(Cur, "side", loc_data).value(); if (0 == strcmp(Prop, "left")) { side = LEFT; } else if (0 == strcmp(Prop, "top")) { side = TOP; } else if (0 == strcmp(Prop, "right")) { side = RIGHT; } else if (0 == strcmp(Prop, "bottom")) { side = BOTTOM; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "'%s' is not a valid side.\n", Prop); } if ((x_offset < 0) || (x_offset >= PhysicalTileType->width)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "'%d' is an invalid horizontal offset for type '%s' (must be within [0, %d]).\n", x_offset, PhysicalTileType->name, PhysicalTileType->width - 1); } if ((y_offset < 0) || (y_offset >= PhysicalTileType->height)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "'%d' is an invalid vertical offset for type '%s' (must be within [0, %d]).\n", y_offset, PhysicalTileType->name, PhysicalTileType->height - 1); } //Check for duplicate side specifications, since the code below silently overwrites if there are duplicates auto side_offset = std::make_tuple(side, x_offset, y_offset); if (seen_sides.count(side_offset)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Duplicate pin location side/offset specification." " Only a single per side/xoffset/yoffset is permitted.\n"); } seen_sides.insert(side_offset); /* Go through lists of pins */ const std::vector Tokens = vtr::split(Cur.child_value()); Count = Tokens.size(); PhysicalTileType->num_pin_loc_assignments[x_offset][y_offset][side] = Count; if (Count > 0) { PhysicalTileType->pin_loc_assignments[x_offset][y_offset][side] = (char**)vtr::calloc(Count, sizeof(char*)); for (int pin = 0; pin < Count; ++pin) { /* Store location assignment */ PhysicalTileType->pin_loc_assignments[x_offset][y_offset][side][pin] = vtr::strdup(Tokens[pin].c_str()); /* Advance through list of pins in this location */ } } Cur = Cur.next_sibling(Cur.name()); } //Verify that all top-level pins have had thier locations specified //Record all the specified pins std::map> port_pins_with_specified_locations; for (int w = 0; w < PhysicalTileType->width; ++w) { for (int h = 0; h < PhysicalTileType->height; ++h) { for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { for (int itoken = 0; itoken < PhysicalTileType->num_pin_loc_assignments[w][h][side]; ++itoken) { const char* pin_spec = PhysicalTileType->pin_loc_assignments[w][h][side][itoken]; InstPort inst_port(PhysicalTileType->pin_loc_assignments[w][h][side][itoken]); //A pin specification should contain only the block name, and not any instace count information if (inst_port.instance_low_index() != InstPort::UNSPECIFIED || inst_port.instance_high_index() != InstPort::UNSPECIFIED) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "Pin location specification '%s' should not contain an instance range (should only be the block name)", pin_spec); } //Check that the block name matches if (inst_port.instance_name() != PhysicalTileType->name) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "Mismatched block name in pin location specification (expected '%s' was '%s')", PhysicalTileType->name, inst_port.instance_name().c_str()); } int pin_low_idx = inst_port.port_low_index(); int pin_high_idx = inst_port.port_high_index(); if (pin_low_idx == InstPort::UNSPECIFIED && pin_high_idx == InstPort::UNSPECIFIED) { //Empty range, so full port //Find the matching pb type to get the total number of pins const t_physical_tile_port* port = nullptr; for (const auto& tmp_port : PhysicalTileType->ports) { if (tmp_port.name == inst_port.port_name()) { port = &tmp_port; break; } } if (port) { pin_low_idx = 0; pin_high_idx = port->num_pins - 1; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "Failed to find port named '%s' on block '%s'", inst_port.port_name().c_str(), PhysicalTileType->name); } } VTR_ASSERT(pin_low_idx >= 0); VTR_ASSERT(pin_high_idx >= 0); for (int ipin = pin_low_idx; ipin <= pin_high_idx; ++ipin) { //Record that the pin has it's location specified port_pins_with_specified_locations[inst_port.port_name()].insert(ipin); } } } } } //Check for any pins missing location specs for (const auto& port : PhysicalTileType->ports) { for (int ipin = 0; ipin < port.num_pins; ++ipin) { if (!port_pins_with_specified_locations[port.name].count(ipin)) { //Missing archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "Pin '%s.%s[%d]' has no pin location specificed (a location is required for pattern=\"custom\")", PhysicalTileType->name, port.name, ipin); } } } } else if (Locations) { //Non-custom pin locations. There should be no child tags expect_child_node_count(Locations, 0, loc_data); } /* Setup pin classes */ num_class = 0; for (const auto& port : PhysicalTileType->ports) { if (port.equivalent != PortEquivalence::NONE) { num_class += capacity; } else { num_class += capacity * port.num_pins; } } PhysicalTileType->class_inf = (t_class*)vtr::calloc(num_class, sizeof(t_class)); PhysicalTileType->num_class = num_class; PhysicalTileType->pin_class = (int*)vtr::malloc(PhysicalTileType->num_pins * sizeof(int) * capacity); PhysicalTileType->is_ignored_pin = (bool*)vtr::malloc(PhysicalTileType->num_pins * sizeof(bool) * capacity); PhysicalTileType->is_pin_global = (bool*)vtr::malloc(PhysicalTileType->num_pins * sizeof(bool) * capacity); for (i = 0; i < PhysicalTileType->num_pins * capacity; i++) { PhysicalTileType->pin_class[i] = OPEN; PhysicalTileType->is_ignored_pin[i] = true; PhysicalTileType->is_pin_global[i] = true; } pin_count = 0; /* Equivalent pins share the same class, non-equivalent pins belong to different pin classes */ num_class = 0; for (i = 0; i < capacity; ++i) { for (const auto& port : PhysicalTileType->ports) { if (port.equivalent != PortEquivalence::NONE) { PhysicalTileType->class_inf[num_class].num_pins = port.num_pins; PhysicalTileType->class_inf[num_class].pinlist = (int*)vtr::malloc(sizeof(int) * port.num_pins); PhysicalTileType->class_inf[num_class].equivalence = port.equivalent; } for (k = 0; k < port.num_pins; ++k) { if (port.equivalent == PortEquivalence::NONE) { PhysicalTileType->class_inf[num_class].num_pins = 1; PhysicalTileType->class_inf[num_class].pinlist = (int*)vtr::malloc(sizeof(int) * 1); PhysicalTileType->class_inf[num_class].pinlist[0] = pin_count; } else { PhysicalTileType->class_inf[num_class].pinlist[k] = pin_count; } if (port.type == IN_PORT) { PhysicalTileType->class_inf[num_class].type = RECEIVER; } else { VTR_ASSERT(port.type == OUT_PORT); PhysicalTileType->class_inf[num_class].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; pin_count++; if (port.equivalent == PortEquivalence::NONE) { num_class++; } } if (port.equivalent != PortEquivalence::NONE) { num_class++; } } } VTR_ASSERT(num_class == PhysicalTileType->num_class); VTR_ASSERT(pin_count == PhysicalTileType->num_pins); } static void LoadPinLoc(pugi::xml_node Locations, t_physical_tile_type* type, const pugiutil::loc_data& loc_data) { type->pin_width_offset.resize(type->num_pins, 0); type->pin_height_offset.resize(type->num_pins, 0); std::vector physical_pin_counts(type->num_pins, 0); if (type->pin_location_distribution == E_SPREAD_PIN_DISTR) { /* evenly distribute pins starting at bottom left corner */ int num_sides = 4 * (type->width * type->height); int side_index = 0; int count = 0; for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { for (int width = 0; width < type->width; ++width) { for (int height = 0; height < type->height; ++height) { for (int pin_offset = 0; pin_offset < (type->num_pins / num_sides) + 1; ++pin_offset) { int pin_num = side_index + pin_offset * num_sides; if (pin_num < type->num_pins) { type->pinloc[width][height][side][pin_num] = true; type->pin_width_offset[pin_num] += width; type->pin_height_offset[pin_num] += height; physical_pin_counts[pin_num] += 1; count++; } } side_index++; } } } VTR_ASSERT(side_index == num_sides); VTR_ASSERT(count == type->num_pins); } else if (type->pin_location_distribution == E_PERIMETER_PIN_DISTR) { //Add one pin at-a-time to perimeter sides in round-robin order int ipin = 0; while (ipin < type->num_pins) { for (int width = 0; width < type->width; ++width) { for (int height = 0; height < type->height; ++height) { for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { if (((width == 0 && side == LEFT) || (height == type->height - 1 && side == TOP) || (width == type->width - 1 && side == RIGHT) || (height == 0 && side == BOTTOM)) && ipin < type->num_pins) { //On a side, with pins still to allocate type->pinloc[width][height][side][ipin] = true; type->pin_width_offset[ipin] += width; type->pin_height_offset[ipin] += height; physical_pin_counts[ipin] += 1; ++ipin; } } } } } VTR_ASSERT(ipin == type->num_pins); } else if (type->pin_location_distribution == E_SPREAD_INPUTS_PERIMETER_OUTPUTS_PIN_DISTR) { //Collect the sets of block input/output pins std::vector input_pins; std::vector output_pins; for (int pin_num = 0; pin_num < type->num_pins; ++pin_num) { int iclass = type->pin_class[pin_num]; if (type->class_inf[iclass].type == RECEIVER) { input_pins.push_back(pin_num); } else { VTR_ASSERT(type->class_inf[iclass].type == DRIVER); output_pins.push_back(pin_num); } } //Allocate the inputs one pin at-a-time in a round-robin order //to all sides size_t ipin = 0; while (ipin < input_pins.size()) { for (int width = 0; width < type->width; ++width) { for (int height = 0; height < type->height; ++height) { for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { if (ipin < input_pins.size()) { //Pins still to allocate int pin_num = input_pins[ipin]; type->pinloc[width][height][side][pin_num] = true; type->pin_width_offset[pin_num] += width; type->pin_height_offset[pin_num] += height; physical_pin_counts[pin_num] += 1; ++ipin; } } } } } VTR_ASSERT(ipin == input_pins.size()); //Allocate the outputs one pin at-a-time to perimeter sides in round-robin order ipin = 0; while (ipin < output_pins.size()) { for (int width = 0; width < type->width; ++width) { for (int height = 0; height < type->height; ++height) { for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { if (((width == 0 && side == LEFT) || (height == type->height - 1 && side == TOP) || (width == type->width - 1 && side == RIGHT) || (height == 0 && side == BOTTOM)) && ipin < output_pins.size()) { //On a perimeter side, with pins still to allocate int pin_num = output_pins[ipin]; type->pinloc[width][height][side][pin_num] = true; type->pin_width_offset[pin_num] += width; type->pin_height_offset[pin_num] += height; physical_pin_counts[pin_num] += 1; ++ipin; } } } } } VTR_ASSERT(ipin == output_pins.size()); } else { VTR_ASSERT(type->pin_location_distribution == E_CUSTOM_PIN_DISTR); for (int width = 0; width < type->width; ++width) { for (int height = 0; height < type->height; ++height) { for (e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { for (int pin = 0; pin < type->num_pin_loc_assignments[width][height][side]; ++pin) { auto pin_range = ProcessPinString(Locations, type, type->pin_loc_assignments[width][height][side][pin], loc_data); for (int pin_num = pin_range.first; pin_num < pin_range.second; ++pin_num) { VTR_ASSERT(pin_num < type->num_pins / type->capacity); for (int capacity = 0; capacity < type->capacity; ++capacity) { type->pinloc[width][height][side][pin_num + capacity * type->num_pins / type->capacity] = true; type->pin_width_offset[pin_num + capacity * type->num_pins / type->capacity] += width; type->pin_height_offset[pin_num + capacity * type->num_pins / type->capacity] += height; physical_pin_counts[pin_num + capacity * type->num_pins / type->capacity] += 1; } } } } } } } for (int ipin = 0; ipin < type->num_pins; ++ipin) { VTR_ASSERT(physical_pin_counts[ipin] >= 1); type->pin_width_offset[ipin] /= physical_pin_counts[ipin]; type->pin_height_offset[ipin] /= physical_pin_counts[ipin]; VTR_ASSERT(type->pin_width_offset[ipin] >= 0 && type->pin_width_offset[ipin] < type->width); VTR_ASSERT(type->pin_height_offset[ipin] >= 0 && type->pin_height_offset[ipin] < type->height); } } template static std::pair ProcessPinString(pugi::xml_node Locations, T type, const char* pin_loc_string, const pugiutil::loc_data& loc_data) { int num_tokens; auto tokens = GetTokensFromString(pin_loc_string, &num_tokens); int token_index = 0; auto token = tokens[token_index]; if (token.type != TOKEN_STRING || 0 != strcmp(token.data, type->name)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "Wrong physical type name of the port: %s\n", pin_loc_string); } token_index++; token = tokens[token_index]; if (token.type != TOKEN_DOT) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "No dot is present to separate type name and port name: %s\n", pin_loc_string); } token_index++; token = tokens[token_index]; if (token.type != TOKEN_STRING) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "No port name is present: %s\n", pin_loc_string); } auto port = get_port_by_name(type, token.data); if (port == nullptr) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "Port %s for %s could not be found: %s\n", type->name, token.data, pin_loc_string); } int abs_first_pin_idx = port->absolute_first_pin_index; token_index++; // All the pins of the port are taken or the port has a single pin if (token_index == num_tokens) { freeTokens(tokens, num_tokens); return std::make_pair(abs_first_pin_idx, abs_first_pin_idx + port->num_pins); } token = tokens[token_index]; if (token.type != TOKEN_OPEN_SQUARE_BRACKET) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "No open square bracket present: %s\n", pin_loc_string); } token_index++; token = tokens[token_index]; if (token.type != TOKEN_INT) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "No integer to indicate least significant pin index: %s\n", pin_loc_string); } int first_pin = vtr::atoi(token.data); token_index++; token = tokens[token_index]; // Single pin is specified if (token.type != TOKEN_COLON) { if (token.type != TOKEN_CLOSE_SQUARE_BRACKET) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "No closing bracket: %s\n", pin_loc_string); } token_index++; if (token_index != num_tokens) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "pin location should be completed, but more tokens are present: %s\n", pin_loc_string); } freeTokens(tokens, num_tokens); return std::make_pair(abs_first_pin_idx + first_pin, abs_first_pin_idx + first_pin + 1); } token_index++; token = tokens[token_index]; if (token.type != TOKEN_INT) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "No integer to indicate most significant pin index: %s\n", pin_loc_string); } int last_pin = vtr::atoi(token.data); token_index++; token = tokens[token_index]; if (token.type != TOKEN_CLOSE_SQUARE_BRACKET) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "No closed square bracket: %s\n", pin_loc_string); } token_index++; if (token_index != num_tokens) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "pin location should be completed, but more tokens are present: %s\n", pin_loc_string); } if (first_pin > last_pin) { std::swap(first_pin, last_pin); } freeTokens(tokens, num_tokens); return std::make_pair(abs_first_pin_idx + first_pin, abs_first_pin_idx + last_pin + 1); } static void ProcessPinToPinAnnotations(pugi::xml_node Parent, t_pin_to_pin_annotation* annotation, t_pb_type* parent_pb_type, const pugiutil::loc_data& loc_data) { int i = 0; const char* Prop; if (get_attribute(Parent, "max", loc_data, ReqOpt::OPTIONAL).as_string(nullptr)) { i++; } if (get_attribute(Parent, "min", loc_data, ReqOpt::OPTIONAL).as_string(nullptr)) { i++; } if (get_attribute(Parent, "type", loc_data, ReqOpt::OPTIONAL).as_string(nullptr)) { i++; } if (get_attribute(Parent, "value", loc_data, ReqOpt::OPTIONAL).as_string(nullptr)) { i++; } if (0 == strcmp(Parent.name(), "C_constant") || 0 == strcmp(Parent.name(), "C_matrix") || 0 == strcmp(Parent.name(), "pack_pattern")) { i = 1; } annotation->num_value_prop_pairs = i; annotation->prop = (int*)vtr::calloc(i, sizeof(int)); annotation->value = (char**)vtr::calloc(i, sizeof(char*)); annotation->line_num = loc_data.line(Parent); /* Todo: This is slow, I should use a case lookup */ i = 0; if (0 == strcmp(Parent.name(), "delay_constant")) { annotation->type = E_ANNOT_PIN_TO_PIN_DELAY; annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT; Prop = get_attribute(Parent, "max", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (Prop) { annotation->prop[i] = (int)E_ANNOT_PIN_TO_PIN_DELAY_MAX; annotation->value[i] = vtr::strdup(Prop); i++; } Prop = get_attribute(Parent, "min", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (Prop) { annotation->prop[i] = (int)E_ANNOT_PIN_TO_PIN_DELAY_MIN; annotation->value[i] = vtr::strdup(Prop); i++; } Prop = get_attribute(Parent, "in_port", loc_data).value(); annotation->input_pins = vtr::strdup(Prop); Prop = get_attribute(Parent, "out_port", loc_data).value(); annotation->output_pins = vtr::strdup(Prop); } else if (0 == strcmp(Parent.name(), "delay_matrix")) { annotation->type = E_ANNOT_PIN_TO_PIN_DELAY; annotation->format = E_ANNOT_PIN_TO_PIN_MATRIX; Prop = get_attribute(Parent, "type", loc_data).value(); annotation->value[i] = vtr::strdup(Parent.child_value()); if (0 == strcmp(Prop, "max")) { annotation->prop[i] = (int)E_ANNOT_PIN_TO_PIN_DELAY_MAX; } else { VTR_ASSERT(0 == strcmp(Prop, "min")); annotation->prop[i] = (int)E_ANNOT_PIN_TO_PIN_DELAY_MIN; } i++; Prop = get_attribute(Parent, "in_port", loc_data).value(); annotation->input_pins = vtr::strdup(Prop); Prop = get_attribute(Parent, "out_port", loc_data).value(); annotation->output_pins = vtr::strdup(Prop); } else if (0 == strcmp(Parent.name(), "C_constant")) { annotation->type = E_ANNOT_PIN_TO_PIN_CAPACITANCE; annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT; Prop = get_attribute(Parent, "C", loc_data).value(); annotation->value[i] = vtr::strdup(Prop); annotation->prop[i] = (int)E_ANNOT_PIN_TO_PIN_CAPACITANCE_C; i++; Prop = get_attribute(Parent, "in_port", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); annotation->input_pins = vtr::strdup(Prop); Prop = get_attribute(Parent, "out_port", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); annotation->output_pins = vtr::strdup(Prop); VTR_ASSERT(annotation->output_pins != nullptr || annotation->input_pins != nullptr); } else if (0 == strcmp(Parent.name(), "C_matrix")) { annotation->type = E_ANNOT_PIN_TO_PIN_CAPACITANCE; annotation->format = E_ANNOT_PIN_TO_PIN_MATRIX; annotation->value[i] = vtr::strdup(Parent.child_value()); annotation->prop[i] = (int)E_ANNOT_PIN_TO_PIN_CAPACITANCE_C; i++; Prop = get_attribute(Parent, "in_port", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); annotation->input_pins = vtr::strdup(Prop); Prop = get_attribute(Parent, "out_port", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); annotation->output_pins = vtr::strdup(Prop); VTR_ASSERT(annotation->output_pins != nullptr || annotation->input_pins != nullptr); } else if (0 == strcmp(Parent.name(), "T_setup")) { annotation->type = E_ANNOT_PIN_TO_PIN_DELAY; annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT; Prop = get_attribute(Parent, "value", loc_data).value(); annotation->prop[i] = (int)E_ANNOT_PIN_TO_PIN_DELAY_TSETUP; annotation->value[i] = vtr::strdup(Prop); i++; Prop = get_attribute(Parent, "port", loc_data).value(); annotation->input_pins = vtr::strdup(Prop); Prop = get_attribute(Parent, "clock", loc_data).value(); annotation->clock = vtr::strdup(Prop); primitives_annotation_clock_match(annotation, parent_pb_type); } else if (0 == strcmp(Parent.name(), "T_clock_to_Q")) { annotation->type = E_ANNOT_PIN_TO_PIN_DELAY; annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT; Prop = get_attribute(Parent, "max", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); bool found_min_max_attrib = false; if (Prop) { annotation->prop[i] = (int)E_ANNOT_PIN_TO_PIN_DELAY_CLOCK_TO_Q_MAX; annotation->value[i] = vtr::strdup(Prop); i++; found_min_max_attrib = true; } Prop = get_attribute(Parent, "min", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (Prop) { annotation->prop[i] = (int)E_ANNOT_PIN_TO_PIN_DELAY_CLOCK_TO_Q_MIN; annotation->value[i] = vtr::strdup(Prop); i++; found_min_max_attrib = true; } if (!found_min_max_attrib) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Failed to find either 'max' or 'min' attribute required for <%s> in <%s>", Parent.name(), Parent.parent().name()); } Prop = get_attribute(Parent, "port", loc_data).value(); annotation->input_pins = vtr::strdup(Prop); Prop = get_attribute(Parent, "clock", loc_data).value(); annotation->clock = vtr::strdup(Prop); primitives_annotation_clock_match(annotation, parent_pb_type); } else if (0 == strcmp(Parent.name(), "T_hold")) { annotation->type = E_ANNOT_PIN_TO_PIN_DELAY; annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT; Prop = get_attribute(Parent, "value", loc_data).value(); annotation->prop[i] = (int)E_ANNOT_PIN_TO_PIN_DELAY_THOLD; annotation->value[i] = vtr::strdup(Prop); i++; Prop = get_attribute(Parent, "port", loc_data).value(); annotation->input_pins = vtr::strdup(Prop); Prop = get_attribute(Parent, "clock", loc_data).value(); annotation->clock = vtr::strdup(Prop); primitives_annotation_clock_match(annotation, parent_pb_type); } else if (0 == strcmp(Parent.name(), "pack_pattern")) { annotation->type = E_ANNOT_PIN_TO_PIN_PACK_PATTERN; annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT; Prop = get_attribute(Parent, "name", loc_data).value(); annotation->prop[i] = (int)E_ANNOT_PIN_TO_PIN_PACK_PATTERN_NAME; annotation->value[i] = vtr::strdup(Prop); i++; Prop = get_attribute(Parent, "in_port", loc_data).value(); annotation->input_pins = vtr::strdup(Prop); Prop = get_attribute(Parent, "out_port", loc_data).value(); annotation->output_pins = vtr::strdup(Prop); } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Unknown port type %s in %s in %s", Parent.name(), Parent.parent().name(), Parent.parent().parent().name()); } VTR_ASSERT(i == annotation->num_value_prop_pairs); } static void ProcessPb_TypePowerPinToggle(pugi::xml_node parent, t_pb_type* pb_type, const pugiutil::loc_data& loc_data) { pugi::xml_node cur; const char* prop; t_port* port; int high, low; cur = get_first_child(parent, "port", loc_data, ReqOpt::OPTIONAL); while (cur) { prop = get_attribute(cur, "name", loc_data).value(); port = findPortByName(prop, pb_type, &high, &low); if (!port) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), "Could not find port '%s' needed for energy per toggle.", prop); } if (high != port->num_pins - 1 || low != 0) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), "Pin-toggle does not support pin indices (%s)", prop); } if (port->port_power->pin_toggle_initialized) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), "Duplicate pin-toggle energy for port '%s'", port->name); } port->port_power->pin_toggle_initialized = true; /* Get energy per toggle */ port->port_power->energy_per_toggle = get_attribute(cur, "energy_per_toggle", loc_data) .as_float(0.); /* Get scaled by factor */ bool reverse_scaled = false; prop = get_attribute(cur, "scaled_by_static_prob", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (!prop) { prop = get_attribute(cur, "scaled_by_static_prob_n", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (prop) { reverse_scaled = true; } } if (prop) { port->port_power->scaled_by_port = findPortByName(prop, pb_type, &high, &low); if (high != low) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), "Pin-toggle 'scaled_by_static_prob' must be a single pin (%s)", prop); } port->port_power->scaled_by_port_pin_idx = high; port->port_power->reverse_scaled = reverse_scaled; } cur = cur.next_sibling(cur.name()); } } static void ProcessPb_TypePower(pugi::xml_node Parent, t_pb_type* pb_type, const pugiutil::loc_data& loc_data) { pugi::xml_node cur, child; bool require_dynamic_absolute = false; bool require_static_absolute = false; bool require_dynamic_C_internal = false; cur = get_first_child(Parent, "power", loc_data, ReqOpt::OPTIONAL); if (!cur) { return; } switch (pb_type->pb_type_power->estimation_method) { case POWER_METHOD_TOGGLE_PINS: ProcessPb_TypePowerPinToggle(cur, pb_type, loc_data); require_static_absolute = true; break; case POWER_METHOD_C_INTERNAL: require_dynamic_C_internal = true; require_static_absolute = true; break; case POWER_METHOD_ABSOLUTE: require_dynamic_absolute = true; require_static_absolute = true; break; default: break; } if (require_static_absolute) { child = get_single_child(cur, "static_power", loc_data); pb_type->pb_type_power->absolute_power_per_instance.leakage = get_attribute(child, "power_per_instance", loc_data).as_float(0.); } if (require_dynamic_absolute) { child = get_single_child(cur, "dynamic_power", loc_data); pb_type->pb_type_power->absolute_power_per_instance.dynamic = get_attribute(child, "power_per_instance", loc_data).as_float(0.); } if (require_dynamic_C_internal) { child = get_single_child(cur, "dynamic_power", loc_data); pb_type->pb_type_power->C_internal = get_attribute(child, "C_internal", loc_data) .as_float(0.); } } static void ProcessPb_TypePowerEstMethod(pugi::xml_node Parent, t_pb_type* pb_type, const pugiutil::loc_data& loc_data) { pugi::xml_node cur; const char* prop; e_power_estimation_method parent_power_method; prop = nullptr; cur = get_first_child(Parent, "power", loc_data, ReqOpt::OPTIONAL); if (cur) { prop = get_attribute(cur, "method", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); } if (pb_type->parent_mode && pb_type->parent_mode->parent_pb_type) { parent_power_method = pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method; } else { parent_power_method = POWER_METHOD_AUTO_SIZES; } if (!prop) { /* default method is auto-size */ pb_type->pb_type_power->estimation_method = power_method_inherited(parent_power_method); } else if (strcmp(prop, "auto-size") == 0) { pb_type->pb_type_power->estimation_method = POWER_METHOD_AUTO_SIZES; } else if (strcmp(prop, "specify-size") == 0) { pb_type->pb_type_power->estimation_method = POWER_METHOD_SPECIFY_SIZES; } else if (strcmp(prop, "pin-toggle") == 0) { pb_type->pb_type_power->estimation_method = POWER_METHOD_TOGGLE_PINS; } else if (strcmp(prop, "c-internal") == 0) { pb_type->pb_type_power->estimation_method = POWER_METHOD_C_INTERNAL; } else if (strcmp(prop, "absolute") == 0) { pb_type->pb_type_power->estimation_method = POWER_METHOD_ABSOLUTE; } else if (strcmp(prop, "ignore") == 0) { pb_type->pb_type_power->estimation_method = POWER_METHOD_IGNORE; } else if (strcmp(prop, "sum-of-children") == 0) { pb_type->pb_type_power->estimation_method = POWER_METHOD_SUM_OF_CHILDREN; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), "Invalid power estimation method for pb_type '%s'", pb_type->name); } } /* Takes in a pb_type, allocates and loads data for it and recurses downwards */ static void ProcessPb_Type(pugi::xml_node Parent, t_pb_type* pb_type, t_mode* mode, const bool timing_enabled, const t_arch& arch, const pugiutil::loc_data& loc_data) { int num_ports, i, j, k, num_annotations; const char* Prop; pugi::xml_node Cur; bool is_root_pb_type = !(mode != nullptr && mode->parent_pb_type != nullptr); bool is_leaf_pb_type = bool(get_attribute(Parent, "blif_model", loc_data, ReqOpt::OPTIONAL)); std::vector children_to_expect = {"input", "output", "clock", "mode", "power", "metadata"}; if (!is_leaf_pb_type) { //Non-leafs may have a model/pb_type children children_to_expect.push_back("model"); children_to_expect.push_back("pb_type"); children_to_expect.push_back("interconnect"); if (is_root_pb_type) { VTR_ASSERT(!is_leaf_pb_type); //Top level pb_type's may also have the following tag types children_to_expect.push_back("fc"); children_to_expect.push_back("pinlocations"); children_to_expect.push_back("switchblock_locations"); } } else { VTR_ASSERT(is_leaf_pb_type); VTR_ASSERT(!is_root_pb_type); //Leaf pb_type's may also have the following tag types children_to_expect.push_back("T_setup"); children_to_expect.push_back("T_hold"); children_to_expect.push_back("T_clock_to_Q"); children_to_expect.push_back("delay_constant"); children_to_expect.push_back("delay_matrix"); } //Sanity check contained tags expect_only_children(Parent, children_to_expect, loc_data); char* class_name; /* STL maps for checking various duplicate names */ std::map pb_port_names; std::map mode_names; std::pair::iterator, bool> ret_pb_ports; std::pair::iterator, bool> ret_mode_names; int num_in_ports, num_out_ports, num_clock_ports; int num_delay_constant, num_delay_matrix, num_C_constant, num_C_matrix, num_T_setup, num_T_cq, num_T_hold; pb_type->parent_mode = mode; if (mode != nullptr && mode->parent_pb_type != nullptr) { pb_type->depth = mode->parent_pb_type->depth + 1; Prop = get_attribute(Parent, "name", loc_data).value(); pb_type->name = vtr::strdup(Prop); } else { pb_type->depth = 0; /* same name as type */ } Prop = get_attribute(Parent, "blif_model", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); pb_type->blif_model = vtr::strdup(Prop); pb_type->class_type = UNKNOWN_CLASS; Prop = get_attribute(Parent, "class", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); class_name = vtr::strdup(Prop); if (class_name) { if (0 == strcmp(class_name, PB_TYPE_CLASS_STRING[LUT_CLASS])) { pb_type->class_type = LUT_CLASS; } else if (0 == strcmp(class_name, PB_TYPE_CLASS_STRING[LATCH_CLASS])) { pb_type->class_type = LATCH_CLASS; } else if (0 == strcmp(class_name, PB_TYPE_CLASS_STRING[MEMORY_CLASS])) { pb_type->class_type = MEMORY_CLASS; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Unknown class '%s' in pb_type '%s'\n", class_name, pb_type->name); } free(class_name); } if (mode == nullptr) { pb_type->num_pb = 1; } else { pb_type->num_pb = get_attribute(Parent, "num_pb", loc_data).as_int(0); } VTR_ASSERT(pb_type->num_pb > 0); num_ports = num_in_ports = num_out_ports = num_clock_ports = 0; num_in_ports = count_children(Parent, "input", loc_data, ReqOpt::OPTIONAL); num_out_ports = count_children(Parent, "output", loc_data, ReqOpt::OPTIONAL); num_clock_ports = count_children(Parent, "clock", loc_data, ReqOpt::OPTIONAL); num_ports = num_in_ports + num_out_ports + num_clock_ports; pb_type->ports = (t_port*)vtr::calloc(num_ports, sizeof(t_port)); pb_type->num_ports = num_ports; /* Enforce VPR's definition of LUT/FF by checking number of ports */ if (pb_type->class_type == LUT_CLASS || pb_type->class_type == LATCH_CLASS) { if (num_in_ports != 1 || num_out_ports != 1) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "%s primitives must contain exactly one input port and one output port." "Found '%d' input port(s) and '%d' output port(s) for '%s'", (pb_type->class_type == LUT_CLASS) ? "LUT" : "Latch", num_in_ports, num_out_ports, pb_type->name); } } /* Initialize Power Structure */ pb_type->pb_type_power = (t_pb_type_power*)vtr::calloc(1, sizeof(t_pb_type_power)); ProcessPb_TypePowerEstMethod(Parent, pb_type, loc_data); /* process ports */ j = 0; int absolute_port_first_pin_index = 0; for (i = 0; i < 3; i++) { if (i == 0) { k = 0; Cur = get_first_child(Parent, "input", loc_data, ReqOpt::OPTIONAL); } else if (i == 1) { k = 0; Cur = get_first_child(Parent, "output", loc_data, ReqOpt::OPTIONAL); } else { k = 0; Cur = get_first_child(Parent, "clock", loc_data, ReqOpt::OPTIONAL); } while (Cur) { pb_type->ports[j].parent_pb_type = pb_type; pb_type->ports[j].index = j; pb_type->ports[j].port_index_by_type = k; ProcessPb_TypePort(Cur, &pb_type->ports[j], pb_type->pb_type_power->estimation_method, is_root_pb_type, loc_data); pb_type->ports[j].absolute_first_pin_index = absolute_port_first_pin_index; absolute_port_first_pin_index += pb_type->ports[j].num_pins; //Check port name duplicates ret_pb_ports = pb_port_names.insert(std::pair(pb_type->ports[j].name, 0)); if (!ret_pb_ports.second) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Duplicate port names in pb_type '%s': port '%s'\n", pb_type->name, pb_type->ports[j].name); } /* get next iteration */ j++; k++; Cur = Cur.next_sibling(Cur.name()); } } VTR_ASSERT(j == num_ports); /* Count stats on the number of each type of pin */ pb_type->num_clock_pins = pb_type->num_input_pins = pb_type->num_output_pins = 0; for (i = 0; i < pb_type->num_ports; i++) { if (pb_type->ports[i].type == IN_PORT && pb_type->ports[i].is_clock == false) { pb_type->num_input_pins += pb_type->ports[i].num_pins; } else if (pb_type->ports[i].type == OUT_PORT) { pb_type->num_output_pins += pb_type->ports[i].num_pins; } else { VTR_ASSERT(pb_type->ports[i].is_clock && pb_type->ports[i].type == IN_PORT); pb_type->num_clock_pins += pb_type->ports[i].num_pins; } } pb_type->num_pins = pb_type->num_input_pins + pb_type->num_output_pins + pb_type->num_clock_pins; //Warn that max_internal_delay is no longer supported //TODO: eventually remove try { expect_child_node_count(Parent, "max_internal_delay", 0, loc_data); } catch (pugiutil::XmlError& e) { std::string msg = e.what(); msg += ". has been replaced with / between sequential primitive ports."; msg += " Please upgrade your architecture file."; archfpga_throw(e.filename().c_str(), e.line(), msg.c_str()); } pb_type->annotations = nullptr; pb_type->num_annotations = 0; i = 0; /* Determine if this is a leaf or container pb_type */ if (pb_type->blif_model != nullptr) { /* Process delay and capacitance annotations */ num_annotations = 0; num_delay_constant = count_children(Parent, "delay_constant", loc_data, ReqOpt::OPTIONAL); num_delay_matrix = count_children(Parent, "delay_matrix", loc_data, ReqOpt::OPTIONAL); num_C_constant = count_children(Parent, "C_constant", loc_data, ReqOpt::OPTIONAL); num_C_matrix = count_children(Parent, "C_matrix", loc_data, ReqOpt::OPTIONAL); num_T_setup = count_children(Parent, "T_setup", loc_data, ReqOpt::OPTIONAL); num_T_cq = count_children(Parent, "T_clock_to_Q", loc_data, ReqOpt::OPTIONAL); num_T_hold = count_children(Parent, "T_hold", loc_data, ReqOpt::OPTIONAL); num_annotations = num_delay_constant + num_delay_matrix + num_C_constant + num_C_matrix + num_T_setup + num_T_cq + num_T_hold; pb_type->annotations = (t_pin_to_pin_annotation*)vtr::calloc(num_annotations, sizeof(t_pin_to_pin_annotation)); pb_type->num_annotations = num_annotations; j = 0; for (i = 0; i < 7; i++) { if (i == 0) { Cur = get_first_child(Parent, "delay_constant", loc_data, ReqOpt::OPTIONAL); } else if (i == 1) { Cur = get_first_child(Parent, "delay_matrix", loc_data, ReqOpt::OPTIONAL); } else if (i == 2) { Cur = get_first_child(Parent, "C_constant", loc_data, ReqOpt::OPTIONAL); } else if (i == 3) { Cur = get_first_child(Parent, "C_matrix", loc_data, ReqOpt::OPTIONAL); } else if (i == 4) { Cur = get_first_child(Parent, "T_setup", loc_data, ReqOpt::OPTIONAL); } else if (i == 5) { Cur = get_first_child(Parent, "T_clock_to_Q", loc_data, ReqOpt::OPTIONAL); } else if (i == 6) { Cur = get_first_child(Parent, "T_hold", loc_data, ReqOpt::OPTIONAL); } while (Cur) { ProcessPinToPinAnnotations(Cur, &pb_type->annotations[j], pb_type, loc_data); /* get next iteration */ j++; Cur = Cur.next_sibling(Cur.name()); } } VTR_ASSERT(j == num_annotations); if (timing_enabled) { check_leaf_pb_model_timing_consistency(pb_type, arch); } /* leaf pb_type, if special known class, then read class lib otherwise treat as primitive */ if (pb_type->class_type == LUT_CLASS) { ProcessLutClass(pb_type); } else if (pb_type->class_type == MEMORY_CLASS) { ProcessMemoryClass(pb_type); } else { /* other leaf pb_type do not have modes */ pb_type->num_modes = 0; VTR_ASSERT(count_children(Parent, "mode", loc_data, ReqOpt::OPTIONAL) == 0); } } else { /* container pb_type, process modes */ VTR_ASSERT(pb_type->class_type == UNKNOWN_CLASS); pb_type->num_modes = count_children(Parent, "mode", loc_data, ReqOpt::OPTIONAL); pb_type->pb_type_power->leakage_default_mode = 0; if (pb_type->num_modes == 0) { /* The pb_type operates in an implied one mode */ pb_type->num_modes = 1; pb_type->modes = new t_mode[pb_type->num_modes]; pb_type->modes[i].parent_pb_type = pb_type; pb_type->modes[i].index = i; ProcessMode(Parent, &pb_type->modes[i], timing_enabled, arch, loc_data); i++; } else { pb_type->modes = new t_mode[pb_type->num_modes]; Cur = get_first_child(Parent, "mode", loc_data); while (Cur != nullptr) { if (0 == strcmp(Cur.name(), "mode")) { pb_type->modes[i].parent_pb_type = pb_type; pb_type->modes[i].index = i; ProcessMode(Cur, &pb_type->modes[i], timing_enabled, arch, loc_data); ret_mode_names = mode_names.insert(std::pair(pb_type->modes[i].name, 0)); if (!ret_mode_names.second) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Duplicate mode name: '%s' in pb_type '%s'.\n", pb_type->modes[i].name, pb_type->name); } /* get next iteration */ i++; Cur = Cur.next_sibling(Cur.name()); } } } VTR_ASSERT(i == pb_type->num_modes); } pb_port_names.clear(); mode_names.clear(); pb_type->meta = ProcessMetadata(Parent, loc_data); ProcessPb_TypePower(Parent, pb_type, loc_data); } static void ProcessPb_TypePort_Power(pugi::xml_node Parent, t_port* port, e_power_estimation_method power_method, const pugiutil::loc_data& loc_data) { pugi::xml_node cur; const char* prop; bool wire_defined = false; port->port_power = (t_port_power*)vtr::calloc(1, sizeof(t_port_power)); //Defaults if (power_method == POWER_METHOD_AUTO_SIZES) { port->port_power->wire_type = POWER_WIRE_TYPE_AUTO; port->port_power->buffer_type = POWER_BUFFER_TYPE_AUTO; } else if (power_method == POWER_METHOD_SPECIFY_SIZES) { port->port_power->wire_type = POWER_WIRE_TYPE_IGNORED; port->port_power->buffer_type = POWER_BUFFER_TYPE_NONE; } cur = get_single_child(Parent, "power", loc_data, ReqOpt::OPTIONAL); if (cur) { /* Wire capacitance */ /* Absolute C provided */ prop = get_attribute(cur, "wire_capacitance", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (prop) { if (!(power_method == POWER_METHOD_AUTO_SIZES || power_method == POWER_METHOD_SPECIFY_SIZES)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), "Wire capacitance defined for port '%s'. This is an invalid option for the parent pb_type '%s' power estimation method.", port->name, port->parent_pb_type->name); } else { wire_defined = true; port->port_power->wire_type = POWER_WIRE_TYPE_C; port->port_power->wire.C = (float)atof(prop); } } /* Wire absolute length provided */ prop = get_attribute(cur, "wire_length", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (prop) { if (!(power_method == POWER_METHOD_AUTO_SIZES || power_method == POWER_METHOD_SPECIFY_SIZES)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), "Wire length defined for port '%s'. This is an invalid option for the parent pb_type '%s' power estimation method.", port->name, port->parent_pb_type->name); } else if (wire_defined) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), "Multiple wire properties defined for port '%s', pb_type '%s'.", port->name, port->parent_pb_type->name); } else if (strcmp(prop, "auto") == 0) { wire_defined = true; port->port_power->wire_type = POWER_WIRE_TYPE_AUTO; } else { wire_defined = true; port->port_power->wire_type = POWER_WIRE_TYPE_ABSOLUTE_LENGTH; port->port_power->wire.absolute_length = (float)atof(prop); } } /* Wire relative length provided */ prop = get_attribute(cur, "wire_relative_length", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (prop) { if (!(power_method == POWER_METHOD_AUTO_SIZES || power_method == POWER_METHOD_SPECIFY_SIZES)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), "Wire relative length defined for port '%s'. This is an invalid option for the parent pb_type '%s' power estimation method.", port->name, port->parent_pb_type->name); } else if (wire_defined) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), "Multiple wire properties defined for port '%s', pb_type '%s'.", port->name, port->parent_pb_type->name); } else { wire_defined = true; port->port_power->wire_type = POWER_WIRE_TYPE_RELATIVE_LENGTH; port->port_power->wire.relative_length = (float)atof(prop); } } /* Buffer Size */ prop = get_attribute(cur, "buffer_size", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (prop) { if (!(power_method == POWER_METHOD_AUTO_SIZES || power_method == POWER_METHOD_SPECIFY_SIZES)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), "Buffer size defined for port '%s'. This is an invalid option for the parent pb_type '%s' power estimation method.", port->name, port->parent_pb_type->name); } else if (strcmp(prop, "auto") == 0) { port->port_power->buffer_type = POWER_BUFFER_TYPE_AUTO; } else { port->port_power->buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; port->port_power->buffer_size = (float)atof(prop); } } } } static void ProcessPb_TypePort(pugi::xml_node Parent, t_port* port, e_power_estimation_method power_method, const bool is_root_pb_type, const pugiutil::loc_data& loc_data) { std::vector expected_attributes = {"name", "num_pins", "port_class"}; if (is_root_pb_type) { expected_attributes.push_back("equivalent"); if (Parent.name() == "input"s || Parent.name() == "clock"s) { expected_attributes.push_back("is_non_clock_global"); } } expect_only_attributes(Parent, expected_attributes, loc_data); const char* Prop; Prop = get_attribute(Parent, "name", loc_data).value(); port->name = vtr::strdup(Prop); Prop = get_attribute(Parent, "port_class", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); port->port_class = vtr::strdup(Prop); Prop = get_attribute(Parent, "equivalent", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (Prop) { if (Prop == "none"s) { port->equivalent = PortEquivalence::NONE; } else if (Prop == "full"s) { port->equivalent = PortEquivalence::FULL; } else if (Prop == "instance"s) { if (Parent.name() == "output"s) { port->equivalent = PortEquivalence::INSTANCE; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Invalid pin equivalence '%s' for %s port.", Prop, Parent.name()); } } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Invalid pin equivalence '%s'.", Prop); } } port->num_pins = get_attribute(Parent, "num_pins", loc_data).as_int(0); port->is_non_clock_global = get_attribute(Parent, "is_non_clock_global", loc_data, ReqOpt::OPTIONAL) .as_bool(false); if (port->num_pins <= 0) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Invalid number of pins %d for %s port.", port->num_pins, Parent.name()); } if (0 == strcmp(Parent.name(), "input")) { port->type = IN_PORT; port->is_clock = false; /* Check if LUT/FF port class is lut_in/D */ if (port->parent_pb_type->class_type == LUT_CLASS) { if ((!port->port_class) || strcmp("lut_in", port->port_class)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Inputs to LUT primitives must have a port class named " "as \"lut_in\"."); } } else if (port->parent_pb_type->class_type == LATCH_CLASS) { if ((!port->port_class) || strcmp("D", port->port_class)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Input to flipflop primitives must have a port class named " "as \"D\"."); } /* Only allow one input pin for FF's */ if (port->num_pins != 1) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Input port of flipflop primitives must have exactly one pin. " "Found %d.", port->num_pins); } } } else if (0 == strcmp(Parent.name(), "output")) { port->type = OUT_PORT; port->is_clock = false; /* Check if LUT/FF port class is lut_out/Q */ if (port->parent_pb_type->class_type == LUT_CLASS) { if ((!port->port_class) || strcmp("lut_out", port->port_class)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Output to LUT primitives must have a port class named " "as \"lut_in\"."); } /* Only allow one output pin for LUT's */ if (port->num_pins != 1) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Output port of LUT primitives must have exactly one pin. " "Found %d.", port->num_pins); } } else if (port->parent_pb_type->class_type == LATCH_CLASS) { if ((!port->port_class) || strcmp("Q", port->port_class)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Output to flipflop primitives must have a port class named " "as \"D\"."); } /* Only allow one output pin for FF's */ if (port->num_pins != 1) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Output port of flipflop primitives must have exactly one pin. " "Found %d.", port->num_pins); } } } else if (0 == strcmp(Parent.name(), "clock")) { port->type = IN_PORT; port->is_clock = true; if (port->is_non_clock_global == true) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Port %s cannot be both a clock and a non-clock simultaneously\n", Parent.name()); } if (port->parent_pb_type->class_type == LATCH_CLASS) { if ((!port->port_class) || strcmp("clock", port->port_class)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Clock to flipflop primitives must have a port class named " "as \"clock\"."); } /* Only allow one output pin for FF's */ if (port->num_pins != 1) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Clock port of flipflop primitives must have exactly one pin. " "Found %d.", port->num_pins); } } } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Unknown port type %s", Parent.name()); } ProcessPb_TypePort_Power(Parent, port, power_method, loc_data); } static void ProcessInterconnect(pugi::xml_node Parent, t_mode* mode, const pugiutil::loc_data& loc_data) { int num_interconnect = 0; int num_complete, num_direct, num_mux; int i, j, k, L_index, num_annotations; int num_delay_constant, num_delay_matrix, num_C_constant, num_C_matrix, num_pack_pattern; const char* Prop; pugi::xml_node Cur; pugi::xml_node Cur2; std::map interc_names; std::pair::iterator, bool> ret_interc_names; num_complete = num_direct = num_mux = 0; num_complete = count_children(Parent, "complete", loc_data, ReqOpt::OPTIONAL); num_direct = count_children(Parent, "direct", loc_data, ReqOpt::OPTIONAL); num_mux = count_children(Parent, "mux", loc_data, ReqOpt::OPTIONAL); num_interconnect = num_complete + num_direct + num_mux; mode->num_interconnect = num_interconnect; mode->interconnect = new t_interconnect[num_interconnect]; i = 0; for (L_index = 0; L_index < 3; L_index++) { if (L_index == 0) { Cur = get_first_child(Parent, "complete", loc_data, ReqOpt::OPTIONAL); } else if (L_index == 1) { Cur = get_first_child(Parent, "direct", loc_data, ReqOpt::OPTIONAL); } else { Cur = get_first_child(Parent, "mux", loc_data, ReqOpt::OPTIONAL); } while (Cur != nullptr) { if (0 == strcmp(Cur.name(), "complete")) { mode->interconnect[i].type = COMPLETE_INTERC; } else if (0 == strcmp(Cur.name(), "direct")) { mode->interconnect[i].type = DIRECT_INTERC; } else { VTR_ASSERT(0 == strcmp(Cur.name(), "mux")); mode->interconnect[i].type = MUX_INTERC; } mode->interconnect[i].line_num = loc_data.line(Cur); mode->interconnect[i].parent_mode_index = mode->index; mode->interconnect[i].parent_mode = mode; Prop = get_attribute(Cur, "input", loc_data).value(); mode->interconnect[i].input_string = vtr::strdup(Prop); Prop = get_attribute(Cur, "output", loc_data).value(); mode->interconnect[i].output_string = vtr::strdup(Prop); Prop = get_attribute(Cur, "name", loc_data).value(); mode->interconnect[i].name = vtr::strdup(Prop); mode->interconnect[i].meta = ProcessMetadata(Cur, loc_data); ret_interc_names = interc_names.insert(std::pair(mode->interconnect[i].name, 0)); if (!ret_interc_names.second) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Duplicate interconnect name: '%s' in mode: '%s'.\n", mode->interconnect[i].name, mode->name); } /* Process delay and capacitance annotations */ num_annotations = 0; num_delay_constant = count_children(Cur, "delay_constant", loc_data, ReqOpt::OPTIONAL); num_delay_matrix = count_children(Cur, "delay_matrix", loc_data, ReqOpt::OPTIONAL); num_C_constant = count_children(Cur, "C_constant", loc_data, ReqOpt::OPTIONAL); num_C_matrix = count_children(Cur, "C_matrix", loc_data, ReqOpt::OPTIONAL); num_pack_pattern = count_children(Cur, "pack_pattern", loc_data, ReqOpt::OPTIONAL); num_annotations = num_delay_constant + num_delay_matrix + num_C_constant + num_C_matrix + num_pack_pattern; mode->interconnect[i].annotations = (t_pin_to_pin_annotation*)vtr::calloc(num_annotations, sizeof(t_pin_to_pin_annotation)); mode->interconnect[i].num_annotations = num_annotations; k = 0; for (j = 0; j < 5; j++) { if (j == 0) { Cur2 = get_first_child(Cur, "delay_constant", loc_data, ReqOpt::OPTIONAL); } else if (j == 1) { Cur2 = get_first_child(Cur, "delay_matrix", loc_data, ReqOpt::OPTIONAL); } else if (j == 2) { Cur2 = get_first_child(Cur, "C_constant", loc_data, ReqOpt::OPTIONAL); } else if (j == 3) { Cur2 = get_first_child(Cur, "C_matrix", loc_data, ReqOpt::OPTIONAL); } else if (j == 4) { Cur2 = get_first_child(Cur, "pack_pattern", loc_data, ReqOpt::OPTIONAL); } while (Cur2 != nullptr) { ProcessPinToPinAnnotations(Cur2, &(mode->interconnect[i].annotations[k]), nullptr, loc_data); /* get next iteration */ k++; Cur2 = Cur2.next_sibling(Cur2.name()); } } VTR_ASSERT(k == num_annotations); /* Power */ mode->interconnect[i].interconnect_power = (t_interconnect_power*)vtr::calloc(1, sizeof(t_interconnect_power)); mode->interconnect[i].interconnect_power->port_info_initialized = false; /* get next iteration */ Cur = Cur.next_sibling(Cur.name()); i++; } } interc_names.clear(); VTR_ASSERT(i == num_interconnect); } static void ProcessMode(pugi::xml_node Parent, t_mode* mode, const bool timing_enabled, const t_arch& arch, const pugiutil::loc_data& loc_data) { int i; const char* Prop; pugi::xml_node Cur; std::map pb_type_names; std::pair::iterator, bool> ret_pb_types; bool implied_mode = 0 == strcmp(Parent.name(), "pb_type"); if (implied_mode) { mode->name = vtr::strdup("default"); } else { Prop = get_attribute(Parent, "name", loc_data).value(); mode->name = vtr::strdup(Prop); } /* Xifan Tang: parse XML about if this mode is packable or not */ mode->packable = true; /* If the parent mode is not packable, all the child mode should be unpackable as well */ if (nullptr != mode->parent_pb_type->parent_mode) { mode->packable = mode->parent_pb_type->parent_mode->packable; } /* Override if user specify */ mode->packable = get_attribute(Parent, "packable", loc_data, ReqOpt::OPTIONAL).as_bool(mode->packable); if (false == mode->packable) { VTR_LOG("mode '%s[%s]' is defined by user to be not packable\n", mode->parent_pb_type->name, mode->name); } mode->num_pb_type_children = count_children(Parent, "pb_type", loc_data, ReqOpt::OPTIONAL); if (mode->num_pb_type_children > 0) { mode->pb_type_children = new t_pb_type[mode->num_pb_type_children]; i = 0; Cur = get_first_child(Parent, "pb_type", loc_data); while (Cur != nullptr) { if (0 == strcmp(Cur.name(), "pb_type")) { ProcessPb_Type(Cur, &mode->pb_type_children[i], mode, timing_enabled, arch, loc_data); ret_pb_types = pb_type_names.insert( std::pair(mode->pb_type_children[i].name, 0)); if (!ret_pb_types.second) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Duplicate pb_type name: '%s' in mode: '%s'.\n", mode->pb_type_children[i].name, mode->name); } /* get next iteration */ i++; Cur = Cur.next_sibling(Cur.name()); } } } else { mode->pb_type_children = nullptr; } /* Allocate power structure */ mode->mode_power = (t_mode_power*)vtr::calloc(1, sizeof(t_mode_power)); if (!implied_mode) { // Implied mode metadata is attached to the pb_type, rather than // the t_mode object. mode->meta = ProcessMetadata(Parent, loc_data); } /* Clear STL map used for duplicate checks */ pb_type_names.clear(); Cur = get_single_child(Parent, "interconnect", loc_data); ProcessInterconnect(Cur, mode, loc_data); } static t_metadata_dict ProcessMetadata(pugi::xml_node Parent, const pugiutil::loc_data& loc_data) { // // CLBLL_L_ // t_metadata_dict data; auto metadata = get_single_child(Parent, "metadata", loc_data, ReqOpt::OPTIONAL); if (metadata) { auto meta_tag = get_first_child(metadata, "meta", loc_data); while (meta_tag) { std::string key = get_attribute(meta_tag, "name", loc_data).as_string(); auto value = meta_tag.child_value(); data.add(key, value); meta_tag = meta_tag.next_sibling(meta_tag.name()); } } return data; } static void Process_Fc_Values(pugi::xml_node Node, t_default_fc_spec& spec, const pugiutil::loc_data& loc_data) { spec.specified = true; /* Load the default fc_in */ auto default_fc_in_attrib = get_attribute(Node, "in_type", loc_data); spec.in_value_type = string_to_fc_value_type(default_fc_in_attrib.value(), Node, loc_data); auto in_val_attrib = get_attribute(Node, "in_val", loc_data); spec.in_value = vtr::atof(in_val_attrib.value()); /* Load the default fc_out */ auto default_fc_out_attrib = get_attribute(Node, "out_type", loc_data); spec.out_value_type = string_to_fc_value_type(default_fc_out_attrib.value(), Node, loc_data); auto out_val_attrib = get_attribute(Node, "out_val", loc_data); spec.out_value = vtr::atof(out_val_attrib.value()); } /* Takes in the node ptr for the 'fc' elements and initializes * the appropriate fields of type. */ static void Process_Fc(pugi::xml_node Node, t_physical_tile_type* PhysicalTileType, std::vector& segments, const t_default_fc_spec& arch_def_fc, const pugiutil::loc_data& loc_data) { std::vector fc_overrides; t_default_fc_spec def_fc_spec; if (Node) { /* Load the default Fc values from the node */ Process_Fc_Values(Node, def_fc_spec, loc_data); /* Load any tags */ for (auto child_node : Node.children()) { t_fc_override fc_override = Process_Fc_override(child_node, loc_data); fc_overrides.push_back(fc_override); } } else { /* Use the default value, if available */ if (!arch_def_fc.specified) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), " is missing child , and no specified in architecture\n"); } def_fc_spec = arch_def_fc; } /* Go through all the port/segment combinations and create the (potentially * overriden) pin/seg Fc specifications */ int pins_per_capacity_instance = PhysicalTileType->num_pins / PhysicalTileType->capacity; for (size_t iseg = 0; iseg < segments.size(); ++iseg) { for (int icapacity = 0; icapacity < PhysicalTileType->capacity; ++icapacity) { //If capacity > 0, we need t offset the block index by the number of pins per instance //this ensures that all pins have an Fc specification int iblk_pin = icapacity * pins_per_capacity_instance; for (const auto& port : PhysicalTileType->ports) { t_fc_specification fc_spec; fc_spec.seg_index = iseg; //Apply type and defaults if (port.type == IN_PORT) { fc_spec.fc_type = e_fc_type::IN; fc_spec.fc_value_type = def_fc_spec.in_value_type; fc_spec.fc_value = def_fc_spec.in_value; } else { VTR_ASSERT(port.type == OUT_PORT); fc_spec.fc_type = e_fc_type::OUT; fc_spec.fc_value_type = def_fc_spec.out_value_type; fc_spec.fc_value = def_fc_spec.out_value; } //Apply any matching overrides bool default_overriden = false; for (const auto& fc_override : fc_overrides) { bool apply_override = false; if (!fc_override.port_name.empty() && !fc_override.seg_name.empty()) { //Both port and seg names are specified require exact match on both if (fc_override.port_name == port.name && fc_override.seg_name == segments[iseg].name) { apply_override = true; } } else if (!fc_override.port_name.empty()) { VTR_ASSERT(fc_override.seg_name.empty()); //Only the port name specified, require it to match if (fc_override.port_name == port.name) { apply_override = true; } } else { VTR_ASSERT(!fc_override.seg_name.empty()); VTR_ASSERT(fc_override.port_name.empty()); //Only the seg name specified, require it to match if (fc_override.seg_name == segments[iseg].name) { apply_override = true; } } if (apply_override) { //Exact match, or partial match to either port or seg name // Note that we continue searching, this ensures that the last matching override (in file order) // is applied last if (default_overriden) { //Warn if multiple overrides match VTR_LOGF_WARN(loc_data.filename_c_str(), loc_data.line(Node), "Multiple matching Fc overrides found; the last will be applied\n"); } fc_spec.fc_value_type = fc_override.fc_value_type; fc_spec.fc_value = fc_override.fc_value; default_overriden = true; } } //Add all the pins from this port for (int iport_pin = 0; iport_pin < port.num_pins; ++iport_pin) { //XXX: this assumes that iterating through the tile ports // in order yields the block pin order fc_spec.pins.push_back(iblk_pin); ++iblk_pin; } PhysicalTileType->fc_specs.push_back(fc_spec); } } } } static t_fc_override Process_Fc_override(pugi::xml_node node, const pugiutil::loc_data& loc_data) { if (node.name() != std::string("fc_override")) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), "Unexpeted node of type '%s' (expected optional 'fc_override')", node.name()); } t_fc_override fc_override; expect_child_node_count(node, 0, loc_data); bool seen_fc_type = false; bool seen_fc_value = false; bool seen_port_or_seg = false; for (auto attrib : node.attributes()) { if (attrib.name() == std::string("port_name")) { fc_override.port_name = attrib.value(); seen_port_or_seg |= true; } else if (attrib.name() == std::string("segment_name")) { fc_override.seg_name = attrib.value(); seen_port_or_seg |= true; } else if (attrib.name() == std::string("fc_type")) { fc_override.fc_value_type = string_to_fc_value_type(attrib.value(), node, loc_data); seen_fc_type = true; } else if (attrib.name() == std::string("fc_val")) { fc_override.fc_value = vtr::atof(attrib.value()); seen_fc_value = true; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), "Unexpected attribute '%s'", attrib.name()); } } if (!seen_fc_type) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), "Missing expected attribute 'fc_type'"); } if (!seen_fc_value) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), "Missing expected attribute 'fc_value'"); } if (!seen_port_or_seg) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), "Missing expected attribute(s) 'port_name' and/or 'segment_name'"); } return fc_override; } static e_fc_value_type string_to_fc_value_type(const std::string& str, pugi::xml_node node, const pugiutil::loc_data& loc_data) { e_fc_value_type fc_value_type = e_fc_value_type::FRACTIONAL; if (str == "frac") { fc_value_type = e_fc_value_type::FRACTIONAL; } else if (str == "abs") { fc_value_type = e_fc_value_type::ABSOLUTE; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), "Invalid fc_type '%s'. Must be 'abs' or 'frac'.\n", str.c_str()); } return fc_value_type; } //Process any custom switchblock locations static void ProcessSwitchblockLocations(pugi::xml_node switchblock_locations, t_physical_tile_type* type, const t_arch& arch, const pugiutil::loc_data& loc_data) { VTR_ASSERT(type); expect_only_attributes(switchblock_locations, {"pattern", "internal_switch"}, loc_data); std::string pattern = get_attribute(switchblock_locations, "pattern", loc_data, ReqOpt::OPTIONAL).as_string("external_full_internal_straight"); //Initialize the location specs size_t width = type->width; size_t height = type->height; type->switchblock_locations = vtr::Matrix({{width, height}}, e_sb_type::NONE); type->switchblock_switch_overrides = vtr::Matrix({{width, height}}, DEFAULT_SWITCH); if (pattern == "custom") { expect_only_attributes(switchblock_locations, {"pattern"}, loc_data); //Load a custom pattern specified with tags expect_only_children(switchblock_locations, {"sb_loc"}, loc_data); //Only sb_loc child tags //Default to no SBs unless specified type->switchblock_locations.fill(e_sb_type::NONE); //Track which locations have been assigned to detect overlaps auto assigned_locs = vtr::Matrix({{width, height}}, false); for (pugi::xml_node sb_loc : switchblock_locations.children("sb_loc")) { expect_only_attributes(sb_loc, {"type", "xoffset", "yoffset", "switch_override"}, loc_data); //Determine the type std::string sb_type_str = get_attribute(sb_loc, "type", loc_data, ReqOpt::OPTIONAL).as_string("full"); e_sb_type sb_type = e_sb_type::FULL; if (sb_type_str == "none") { sb_type = e_sb_type::NONE; } else if (sb_type_str == "horizontal") { sb_type = e_sb_type::HORIZONTAL; } else if (sb_type_str == "vertical") { sb_type = e_sb_type::VERTICAL; } else if (sb_type_str == "turns") { sb_type = e_sb_type::TURNS; } else if (sb_type_str == "straight") { sb_type = e_sb_type::STRAIGHT; } else if (sb_type_str == "full") { sb_type = e_sb_type::FULL; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(sb_loc), "Invalid 'type' attribute '%s'\n", sb_type_str.c_str()); } //Determine the switch type int sb_switch_override = DEFAULT_SWITCH; auto sb_switch_override_attr = get_attribute(sb_loc, "switch_override", loc_data, ReqOpt::OPTIONAL); if (sb_switch_override_attr) { std::string sb_switch_override_str = sb_switch_override_attr.as_string(); //Use the specified switch sb_switch_override = find_switch_by_name(arch, sb_switch_override_str); if (sb_switch_override == OPEN) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(switchblock_locations), "Invalid 'switch_override' attribute '%s' (no matching switch named '%s' found)\n", sb_switch_override_str.c_str(), sb_switch_override_str.c_str()); } } //Get the horizontal offset size_t xoffset = get_attribute(sb_loc, "xoffset", loc_data, ReqOpt::OPTIONAL).as_uint(0); if (xoffset > width - 1) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(sb_loc), "Invalid 'xoffset' attribute '%zu' (must be in range [%d,%d])\n", xoffset, 0, width - 1); } //Get the vertical offset size_t yoffset = get_attribute(sb_loc, "yoffset", loc_data, ReqOpt::OPTIONAL).as_uint(0); if (yoffset > height - 1) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(sb_loc), "Invalid 'yoffset' attribute '%zu' (must be in range [%d,%d])\n", yoffset, 0, height - 1); } //Check if this location has already been set if (assigned_locs[xoffset][yoffset]) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(sb_loc), "Duplicate specifications at xoffset=%zu yoffset=%zu\n", xoffset, yoffset); } //Set the custom sb location and type type->switchblock_locations[xoffset][yoffset] = sb_type; type->switchblock_switch_overrides[xoffset][yoffset] = sb_switch_override; assigned_locs[xoffset][yoffset] = true; //Mark the location as set for error detection } } else { //Non-custom patterns //Initialize defaults int internal_switch = DEFAULT_SWITCH; int external_switch = DEFAULT_SWITCH; e_sb_type internal_type = e_sb_type::FULL; e_sb_type external_type = e_sb_type::FULL; //Determine any internal switch override auto internal_switch_attr = get_attribute(switchblock_locations, "internal_switch", loc_data, ReqOpt::OPTIONAL); if (internal_switch_attr) { std::string internal_switch_name = internal_switch_attr.as_string(); //Use the specified switch internal_switch = find_switch_by_name(arch, internal_switch_name); if (internal_switch == OPEN) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(switchblock_locations), "Invalid 'internal_switch' attribute '%s' (no matching switch named '%s' found)\n", internal_switch_name.c_str(), internal_switch_name.c_str()); } } //Identify switch block types if (pattern == "all") { internal_type = e_sb_type::FULL; external_type = e_sb_type::FULL; } else if (pattern == "external") { internal_type = e_sb_type::NONE; external_type = e_sb_type::FULL; } else if (pattern == "internal") { internal_type = e_sb_type::FULL; external_type = e_sb_type::NONE; } else if (pattern == "external_full_internal_straight") { internal_type = e_sb_type::STRAIGHT; external_type = e_sb_type::FULL; } else if (pattern == "none") { internal_type = e_sb_type::NONE; external_type = e_sb_type::NONE; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(switchblock_locations), "Invalid 'pattern' attribute '%s'\n", pattern.c_str()); } //Fill in all locations (sets internal) type->switchblock_locations.fill(internal_type); type->switchblock_switch_overrides.fill(internal_switch); //Fill in top edge external size_t yoffset = height - 1; for (size_t xoffset = 0; xoffset < width; ++xoffset) { type->switchblock_locations[xoffset][yoffset] = external_type; type->switchblock_switch_overrides[xoffset][yoffset] = external_switch; } //Fill in right edge external size_t xoffset = width - 1; for (yoffset = 0; yoffset < height; ++yoffset) { type->switchblock_locations[xoffset][yoffset] = external_type; type->switchblock_switch_overrides[xoffset][yoffset] = external_switch; } } } /* Takes in node pointing to and loads all the * child type objects. */ static void ProcessModels(pugi::xml_node Node, t_arch* arch, const pugiutil::loc_data& loc_data) { pugi::xml_node p; t_model* temp; int L_index; /* std::maps for checking duplicates */ std::map model_name_map; std::pair::iterator, bool> ret_map_name; L_index = NUM_MODELS_IN_LIBRARY; arch->models = nullptr; for (pugi::xml_node model : Node.children()) { //Process each model if (model.name() != std::string("model")) { bad_tag(model, loc_data, Node, {"model"}); } temp = new t_model; temp->index = L_index; L_index++; //Process the tag attributes for (pugi::xml_attribute attr : model.attributes()) { if (attr.name() != std::string("name")) { bad_attribute(attr, model, loc_data); } else { VTR_ASSERT(attr.name() == std::string("name")); if (!temp->name) { //First name attr. seen temp->name = vtr::strdup(attr.value()); } else { //Duplicate name archfpga_throw(loc_data.filename_c_str(), loc_data.line(model), "Duplicate 'name' attribute on tag."); } } } /* Try insert new model, check if already exist at the same time */ ret_map_name = model_name_map.insert(std::pair(temp->name, 0)); if (!ret_map_name.second) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(model), "Duplicate model name: '%s'.\n", temp->name); } //Process the ports std::set port_names; for (pugi::xml_node port_group : model.children()) { if (port_group.name() == std::string("input_ports")) { ProcessModelPorts(port_group, temp, port_names, loc_data); } else if (port_group.name() == std::string("output_ports")) { ProcessModelPorts(port_group, temp, port_names, loc_data); } else { bad_tag(port_group, loc_data, model, {"input_ports", "output_ports"}); } } //Sanity check the model check_model_clocks(model, loc_data, temp); check_model_combinational_sinks(model, loc_data, temp); warn_model_missing_timing(model, loc_data, temp); //Add the model temp->next = arch->models; arch->models = temp; } return; } static void ProcessModelPorts(pugi::xml_node port_group, t_model* model, std::set& port_names, const pugiutil::loc_data& loc_data) { for (pugi::xml_attribute attr : port_group.attributes()) { bad_attribute(attr, port_group, loc_data); } enum PORTS dir = ERR_PORT; if (port_group.name() == std::string("input_ports")) { dir = IN_PORT; } else { VTR_ASSERT(port_group.name() == std::string("output_ports")); dir = OUT_PORT; } //Process each port for (pugi::xml_node port : port_group.children()) { //Should only be ports if (port.name() != std::string("port")) { bad_tag(port, loc_data, port_group, {"port"}); } //Ports should have no children for (pugi::xml_node port_child : port.children()) { bad_tag(port_child, loc_data, port); } t_model_ports* model_port = new t_model_ports; model_port->dir = dir; //Process the attributes of each port for (pugi::xml_attribute attr : port.attributes()) { if (attr.name() == std::string("name")) { model_port->name = vtr::strdup(attr.value()); } else if (attr.name() == std::string("is_clock")) { model_port->is_clock = attribute_to_bool(port, attr, loc_data); } else if (attr.name() == std::string("is_non_clock_global")) { model_port->is_non_clock_global = attribute_to_bool(port, attr, loc_data); } else if (attr.name() == std::string("clock")) { model_port->clock = std::string(attr.value()); } else if (attr.name() == std::string("combinational_sink_ports")) { model_port->combinational_sink_ports = vtr::split(attr.value()); } else { bad_attribute(attr, port, loc_data); } } //Sanity checks if (model_port->is_clock == true && model_port->is_non_clock_global == true) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(port), "Model port '%s' cannot be both a clock and a non-clock signal simultaneously", model_port->name); } if (port_names.count(model_port->name)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(port), "Duplicate model port named '%s'", model_port->name); } if (dir == OUT_PORT && !model_port->combinational_sink_ports.empty()) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(port), "Model output ports can not have combinational sink ports"); } //Add the port if (dir == IN_PORT) { model_port->next = model->inputs; model->inputs = model_port; } else { VTR_ASSERT(dir == OUT_PORT); model_port->next = model->outputs; model->outputs = model_port; } } } static void ProcessLayout(pugi::xml_node layout_tag, t_arch* arch, const pugiutil::loc_data& loc_data) { VTR_ASSERT(layout_tag.name() == std::string("layout")); //Expect only tileable attributes on //expect_only_attributes(layout_tag, {"tileable"}, loc_data); arch->tileable = get_attribute(layout_tag, "tileable", loc_data, ReqOpt::OPTIONAL).as_bool(false); arch->through_channel = get_attribute(layout_tag, "through_channel", loc_data, ReqOpt::OPTIONAL).as_bool(false); //Count the number of or tags size_t auto_layout_cnt = 0; size_t fixed_layout_cnt = 0; for (auto layout_type_tag : layout_tag.children()) { if (layout_type_tag.name() == std::string("auto_layout")) { ++auto_layout_cnt; } else if (layout_type_tag.name() == std::string("fixed_layout")) { ++fixed_layout_cnt; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(layout_type_tag), "Unexpected tag type '<%s>', expected '' or ''", layout_type_tag.name()); } } if (auto_layout_cnt == 0 && fixed_layout_cnt == 0) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(layout_tag), "Expected either an or tag"); } if (auto_layout_cnt > 1) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(layout_tag), "Expected at most one tag"); } VTR_ASSERT_MSG(auto_layout_cnt == 0 || auto_layout_cnt == 1, " may appear at most once"); for (auto layout_type_tag : layout_tag.children()) { t_grid_def grid_def = ProcessGridLayout(layout_type_tag, loc_data); arch->grid_layouts.emplace_back(std::move(grid_def)); } } static t_grid_def ProcessGridLayout(pugi::xml_node layout_type_tag, const pugiutil::loc_data& loc_data) { t_grid_def grid_def; //Determine the grid specification type if (layout_type_tag.name() == std::string("auto_layout")) { expect_only_attributes(layout_type_tag, {"aspect_ratio"}, loc_data); grid_def.grid_type = GridDefType::AUTO; grid_def.aspect_ratio = get_attribute(layout_type_tag, "aspect_ratio", loc_data, ReqOpt::OPTIONAL).as_float(1.); grid_def.name = "auto"; } else if (layout_type_tag.name() == std::string("fixed_layout")) { expect_only_attributes(layout_type_tag, {"width", "height", "name"}, loc_data); grid_def.grid_type = GridDefType::FIXED; grid_def.width = get_attribute(layout_type_tag, "width", loc_data).as_int(); grid_def.height = get_attribute(layout_type_tag, "height", loc_data).as_int(); std::string name = get_attribute(layout_type_tag, "name", loc_data).value(); if (name == "auto") { //We name as 'auto', so don't allow a user to specify it archfpga_throw(loc_data.filename_c_str(), loc_data.line(layout_type_tag), "The name '%s' is reserved for auto-sized layouts; please choose another name"); } grid_def.name = name; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(layout_type_tag), "Unexpected tag '<%s>'. Expected '' or ''.", layout_type_tag.name()); } //Process all the block location specifications for (auto loc_spec_tag : layout_type_tag.children()) { auto loc_type = loc_spec_tag.name(); auto type_name = get_attribute(loc_spec_tag, "type", loc_data).value(); int priority = get_attribute(loc_spec_tag, "priority", loc_data).as_int(); t_metadata_dict meta = ProcessMetadata(loc_spec_tag, loc_data); if (loc_type == std::string("perimeter")) { expect_only_attributes(loc_spec_tag, {"type", "priority"}, loc_data); //The edges t_grid_loc_def left_edge(type_name, priority); //Including corners left_edge.x.start_expr = "0"; left_edge.x.end_expr = "0"; left_edge.y.start_expr = "0"; left_edge.y.end_expr = "H - 1"; t_grid_loc_def right_edge(type_name, priority); //Including corners right_edge.x.start_expr = "W - 1"; right_edge.x.end_expr = "W - 1"; right_edge.y.start_expr = "0"; right_edge.y.end_expr = "H - 1"; t_grid_loc_def bottom_edge(type_name, priority); //Exclucing corners bottom_edge.x.start_expr = "1"; bottom_edge.x.end_expr = "W - 2"; bottom_edge.y.start_expr = "0"; bottom_edge.y.end_expr = "0"; t_grid_loc_def top_edge(type_name, priority); //Excluding corners top_edge.x.start_expr = "1"; top_edge.x.end_expr = "W - 2"; top_edge.y.start_expr = "H - 1"; top_edge.y.end_expr = "H - 1"; left_edge.owned_meta = std::make_unique(meta); left_edge.meta = left_edge.owned_meta.get(); right_edge.meta = left_edge.owned_meta.get(); top_edge.meta = left_edge.owned_meta.get(); bottom_edge.meta = left_edge.owned_meta.get(); grid_def.loc_defs.emplace_back(std::move(left_edge)); grid_def.loc_defs.emplace_back(std::move(right_edge)); grid_def.loc_defs.emplace_back(std::move(top_edge)); grid_def.loc_defs.emplace_back(std::move(bottom_edge)); } else if (loc_type == std::string("corners")) { expect_only_attributes(loc_spec_tag, {"type", "priority"}, loc_data); //The corners t_grid_loc_def bottom_left(type_name, priority); bottom_left.x.start_expr = "0"; bottom_left.x.end_expr = "0"; bottom_left.y.start_expr = "0"; bottom_left.y.end_expr = "0"; t_grid_loc_def top_left(type_name, priority); top_left.x.start_expr = "0"; top_left.x.end_expr = "0"; top_left.y.start_expr = "H-1"; top_left.y.end_expr = "H-1"; t_grid_loc_def bottom_right(type_name, priority); bottom_right.x.start_expr = "W-1"; bottom_right.x.end_expr = "W-1"; bottom_right.y.start_expr = "0"; bottom_right.y.end_expr = "0"; t_grid_loc_def top_right(type_name, priority); top_right.x.start_expr = "W-1"; top_right.x.end_expr = "W-1"; top_right.y.start_expr = "H-1"; top_right.y.end_expr = "H-1"; bottom_left.owned_meta = std::make_unique(meta); bottom_left.meta = bottom_left.owned_meta.get(); top_left.meta = bottom_left.owned_meta.get(); bottom_right.meta = bottom_left.owned_meta.get(); top_right.meta = bottom_left.owned_meta.get(); grid_def.loc_defs.emplace_back(std::move(bottom_left)); grid_def.loc_defs.emplace_back(std::move(top_left)); grid_def.loc_defs.emplace_back(std::move(bottom_right)); grid_def.loc_defs.emplace_back(std::move(top_right)); } else if (loc_type == std::string("fill")) { expect_only_attributes(loc_spec_tag, {"type", "priority"}, loc_data); t_grid_loc_def fill(type_name, priority); fill.x.start_expr = "0"; fill.x.end_expr = "W - 1"; fill.y.start_expr = "0"; fill.y.end_expr = "H - 1"; fill.owned_meta = std::make_unique(meta); fill.meta = fill.owned_meta.get(); grid_def.loc_defs.emplace_back(std::move(fill)); } else if (loc_type == std::string("single")) { expect_only_attributes(loc_spec_tag, {"type", "priority", "x", "y"}, loc_data); t_grid_loc_def single(type_name, priority); single.x.start_expr = get_attribute(loc_spec_tag, "x", loc_data).value(); single.y.start_expr = get_attribute(loc_spec_tag, "y", loc_data).value(); 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(meta); single.meta = single.owned_meta.get(); grid_def.loc_defs.emplace_back(std::move(single)); } else if (loc_type == std::string("col")) { expect_only_attributes(loc_spec_tag, {"type", "priority", "startx", "repeatx", "starty", "incry"}, loc_data); t_grid_loc_def col(type_name, priority); auto startx_attr = get_attribute(loc_spec_tag, "startx", loc_data); col.x.start_expr = startx_attr.value(); col.x.end_expr = startx_attr.value() + std::string(" + w - 1"); //end is inclusive so need to include block width auto repeat_attr = get_attribute(loc_spec_tag, "repeatx", loc_data, ReqOpt::OPTIONAL); if (repeat_attr) { col.x.repeat_expr = repeat_attr.value(); } auto starty_attr = get_attribute(loc_spec_tag, "starty", loc_data, ReqOpt::OPTIONAL); if (starty_attr) { col.y.start_expr = starty_attr.value(); } auto incry_attr = get_attribute(loc_spec_tag, "incry", loc_data, ReqOpt::OPTIONAL); if (incry_attr) { col.y.incr_expr = incry_attr.value(); } col.owned_meta = std::make_unique(meta); col.meta = col.owned_meta.get(); grid_def.loc_defs.emplace_back(std::move(col)); } else if (loc_type == std::string("row")) { expect_only_attributes(loc_spec_tag, {"type", "priority", "starty", "repeaty", "startx", "incrx"}, loc_data); t_grid_loc_def row(type_name, priority); auto starty_attr = get_attribute(loc_spec_tag, "starty", loc_data); row.y.start_expr = starty_attr.value(); row.y.end_expr = starty_attr.value() + std::string(" + h - 1"); //end is inclusive so need to include block height auto repeat_attr = get_attribute(loc_spec_tag, "repeaty", loc_data, ReqOpt::OPTIONAL); if (repeat_attr) { row.y.repeat_expr = repeat_attr.value(); } auto startx_attr = get_attribute(loc_spec_tag, "startx", loc_data, ReqOpt::OPTIONAL); if (startx_attr) { row.x.start_expr = startx_attr.value(); } auto incrx_attr = get_attribute(loc_spec_tag, "incrx", loc_data, ReqOpt::OPTIONAL); if (incrx_attr) { row.x.incr_expr = incrx_attr.value(); } row.owned_meta = std::make_unique(meta); row.meta = row.owned_meta.get(); grid_def.loc_defs.emplace_back(std::move(row)); } else if (loc_type == std::string("region")) { expect_only_attributes(loc_spec_tag, {"type", "priority", "startx", "endx", "repeatx", "incrx", "starty", "endy", "repeaty", "incry"}, loc_data); t_grid_loc_def region(type_name, priority); auto startx_attr = get_attribute(loc_spec_tag, "startx", loc_data, ReqOpt::OPTIONAL); if (startx_attr) { region.x.start_expr = startx_attr.value(); } auto endx_attr = get_attribute(loc_spec_tag, "endx", loc_data, ReqOpt::OPTIONAL); if (endx_attr) { region.x.end_expr = endx_attr.value(); } auto starty_attr = get_attribute(loc_spec_tag, "starty", loc_data, ReqOpt::OPTIONAL); if (starty_attr) { region.y.start_expr = starty_attr.value(); } auto endy_attr = get_attribute(loc_spec_tag, "endy", loc_data, ReqOpt::OPTIONAL); if (endy_attr) { region.y.end_expr = endy_attr.value(); } auto repeatx_attr = get_attribute(loc_spec_tag, "repeatx", loc_data, ReqOpt::OPTIONAL); if (repeatx_attr) { region.x.repeat_expr = repeatx_attr.value(); } auto repeaty_attr = get_attribute(loc_spec_tag, "repeaty", loc_data, ReqOpt::OPTIONAL); if (repeaty_attr) { region.y.repeat_expr = repeaty_attr.value(); } auto incrx_attr = get_attribute(loc_spec_tag, "incrx", loc_data, ReqOpt::OPTIONAL); if (incrx_attr) { region.x.incr_expr = incrx_attr.value(); } auto incry_attr = get_attribute(loc_spec_tag, "incry", loc_data, ReqOpt::OPTIONAL); if (incry_attr) { region.y.incr_expr = incry_attr.value(); } region.owned_meta = std::make_unique(meta); region.meta = region.owned_meta.get(); grid_def.loc_defs.emplace_back(std::move(region)); } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(loc_spec_tag), "Unrecognized grid location specification type '%s'\n", loc_type); } } //Warn if any type has no grid location specifed return grid_def; } /* Takes in node pointing to and loads all the * child type objects. */ static void ProcessDevice(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& arch_def_fc, const pugiutil::loc_data& loc_data) { const char* Prop; pugi::xml_node Cur; bool custom_switch_block = false; //Warn that is no longer supported //TODO: eventually remove try { expect_child_node_count(Node, "timing", 0, loc_data); } catch (pugiutil::XmlError& e) { std::string msg = e.what(); msg += ". has been replaced with the tag."; msg += " Please upgrade your architecture file."; archfpga_throw(e.filename().c_str(), e.line(), msg.c_str()); } expect_only_children(Node, {"sizing", "area", "chan_width_distr", "switch_block", "connection_block", "default_fc"}, loc_data); // tag Cur = get_single_child(Node, "sizing", loc_data); expect_only_attributes(Cur, {"R_minW_nmos", "R_minW_pmos"}, loc_data); arch->R_minW_nmos = get_attribute(Cur, "R_minW_nmos", loc_data).as_float(); arch->R_minW_pmos = get_attribute(Cur, "R_minW_pmos", loc_data).as_float(); // tag Cur = get_single_child(Node, "area", loc_data); expect_only_attributes(Cur, {"grid_logic_tile_area"}, loc_data); arch->grid_logic_tile_area = get_attribute(Cur, "grid_logic_tile_area", loc_data, ReqOpt::OPTIONAL) .as_float(0); // tag Cur = get_single_child(Node, "chan_width_distr", loc_data, ReqOpt::OPTIONAL); expect_only_attributes(Cur, {}, loc_data); if (Cur != nullptr) { ProcessChanWidthDistr(Cur, arch, loc_data); } // tag Cur = get_single_child(Node, "connection_block", loc_data); expect_only_attributes(Cur, {"input_switch_name"}, loc_data); arch->ipin_cblock_switch_name = get_attribute(Cur, "input_switch_name", loc_data).as_string(); // tag Cur = get_single_child(Node, "switch_block", loc_data); //expect_only_attributes(Cur, {"type", "fs", "sub_type", "sub_fs"}, loc_data); Prop = get_attribute(Cur, "type", loc_data).value(); if (strcmp(Prop, "wilton") == 0) { arch->SBType = WILTON; } else if (strcmp(Prop, "universal") == 0) { arch->SBType = UNIVERSAL; } else if (strcmp(Prop, "subset") == 0) { arch->SBType = SUBSET; } else if (strcmp(Prop, "custom") == 0) { arch->SBType = CUSTOM; custom_switch_block = true; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Unknown property %s for switch block type x\n", Prop); } std::string sub_type_str = get_attribute(Cur, "sub_type", loc_data, BoolToReqOpt(false)).as_string(""); /* If not specified, we set the same value as 'type' */ if (!sub_type_str.empty()) { if (sub_type_str == std::string("wilton")) { arch->SBSubType = WILTON; } else if (sub_type_str == std::string("universal")) { arch->SBSubType = UNIVERSAL; } else if (sub_type_str == std::string("subset")) { arch->SBSubType = SUBSET; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Unknown property %s for switch block subtype x\n", Prop); } } else { arch->SBSubType = arch->SBType; } ReqOpt CUSTOM_SWITCHBLOCK_REQD = BoolToReqOpt(!custom_switch_block); arch->Fs = get_attribute(Cur, "fs", loc_data, CUSTOM_SWITCHBLOCK_REQD).as_int(3); arch->subFs = get_attribute(Cur, "sub_fs", loc_data, BoolToReqOpt(false)).as_int(arch->Fs); Cur = get_single_child(Node, "default_fc", loc_data, ReqOpt::OPTIONAL); if (Cur) { arch_def_fc.specified = true; expect_only_attributes(Cur, {"in_type", "in_val", "out_type", "out_val"}, loc_data); Process_Fc_Values(Cur, arch_def_fc, loc_data); } else { arch_def_fc.specified = false; } } /* Takes in node pointing to and loads all the * child type objects. */ static void ProcessChanWidthDistr(pugi::xml_node Node, t_arch* arch, const pugiutil::loc_data& loc_data) { pugi::xml_node Cur; expect_only_children(Node, {"x", "y"}, loc_data); Cur = get_single_child(Node, "x", loc_data); ProcessChanWidthDistrDir(Cur, &arch->Chans.chan_x_dist, loc_data); Cur = get_single_child(Node, "y", loc_data); ProcessChanWidthDistrDir(Cur, &arch->Chans.chan_y_dist, loc_data); } /* Takes in node within and loads all the * child type objects. */ static void ProcessChanWidthDistrDir(pugi::xml_node Node, t_chan* chan, const pugiutil::loc_data& loc_data) { const char* Prop; ReqOpt hasXpeak, hasWidth, hasDc; hasXpeak = hasWidth = hasDc = ReqOpt::OPTIONAL; Prop = get_attribute(Node, "distr", loc_data).value(); if (strcmp(Prop, "uniform") == 0) { chan->type = UNIFORM; } else if (strcmp(Prop, "gaussian") == 0) { chan->type = GAUSSIAN; hasXpeak = hasWidth = hasDc = ReqOpt::REQUIRED; } else if (strcmp(Prop, "pulse") == 0) { chan->type = PULSE; hasXpeak = hasWidth = hasDc = ReqOpt::REQUIRED; } else if (strcmp(Prop, "delta") == 0) { hasXpeak = hasDc = ReqOpt::REQUIRED; chan->type = DELTA; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Unknown property %s for chan_width_distr x\n", Prop); } chan->peak = get_attribute(Node, "peak", loc_data).as_float(UNDEFINED); chan->width = get_attribute(Node, "width", loc_data, hasWidth).as_float(0); chan->xpeak = get_attribute(Node, "xpeak", loc_data, hasXpeak).as_float(0); chan->dc = get_attribute(Node, "dc", loc_data, hasDc).as_float(0); } static void ProcessTiles(pugi::xml_node Node, std::vector& PhysicalTileTypes, std::vector& LogicalBlockTypes, const t_default_fc_spec& arch_def_fc, t_arch& arch, const pugiutil::loc_data& loc_data) { pugi::xml_node CurTileType; pugi::xml_node Cur; std::map tile_type_descriptors; /* Alloc the type list. Need one additional t_type_desctiptors: * 1: empty psuedo-type */ t_physical_tile_type EMPTY_PHYSICAL_TILE_TYPE = SetupEmptyPhysicalType(); EMPTY_PHYSICAL_TILE_TYPE.index = 0; PhysicalTileTypes.push_back(EMPTY_PHYSICAL_TILE_TYPE); /* Process the types */ int index = 1; /* Skip over 'empty' type */ CurTileType = Node.first_child(); while (CurTileType) { check_node(CurTileType, "tile", loc_data); t_physical_tile_type PhysicalTileType; PhysicalTileType.index = index; /* Parses the properties fields of the type */ ProcessTileProps(CurTileType, &PhysicalTileType, loc_data); auto result = tile_type_descriptors.insert(std::pair(PhysicalTileType.name, 0)); if (!result.second) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(CurTileType), "Duplicate tile descriptor name: '%s'.\n", PhysicalTileType.name); } /* Process tile port definitions */ ProcessTilePorts(CurTileType, &PhysicalTileType, loc_data); PhysicalTileType.num_pins = PhysicalTileType.capacity * (PhysicalTileType.num_input_pins + PhysicalTileType.num_output_pins + PhysicalTileType.num_clock_pins); PhysicalTileType.num_receivers = PhysicalTileType.capacity * PhysicalTileType.num_input_pins; PhysicalTileType.num_drivers = PhysicalTileType.capacity * PhysicalTileType.num_output_pins; /* Assign Fc, Pin locations ans Switch Block locations to the Physical Tile Type */ /* Load pin names and classes and locations */ Cur = get_single_child(CurTileType, "pinlocations", loc_data, ReqOpt::OPTIONAL); SetupPinLocationsAndPinClasses(Cur, &PhysicalTileType, loc_data); LoadPinLoc(Cur, &PhysicalTileType, loc_data); //Warn that gridlocations is no longer supported //TODO: eventually remove try { expect_child_node_count(CurTileType, "gridlocations", 0, loc_data); } catch (pugiutil::XmlError& e) { std::string msg = e.what(); msg += ". has been replaced by the and tags in the section."; msg += " Please upgrade your architecture file."; archfpga_throw(e.filename().c_str(), e.line(), msg.c_str()); } /* Load Fc */ Cur = get_single_child(CurTileType, "fc", loc_data, ReqOpt::OPTIONAL); Process_Fc(Cur, &PhysicalTileType, arch.Segments, arch_def_fc, loc_data); //Load switchblock type and location overrides Cur = get_single_child(CurTileType, "switchblock_locations", loc_data, ReqOpt::OPTIONAL); ProcessSwitchblockLocations(Cur, &PhysicalTileType, arch, loc_data); //Load equivalent sites infromation Cur = get_single_child(CurTileType, "equivalent_sites", loc_data, ReqOpt::REQUIRED); ProcessTileEquivalentSites(Cur, &PhysicalTileType, LogicalBlockTypes, loc_data); /* Type fully read */ ++index; /* Push newly created Types to corresponding vectors */ PhysicalTileTypes.push_back(PhysicalTileType); /* Free this node and get its next sibling node */ CurTileType = CurTileType.next_sibling(CurTileType.name()); } tile_type_descriptors.clear(); } static void ProcessTileProps(pugi::xml_node Node, t_physical_tile_type* PhysicalTileType, const pugiutil::loc_data& loc_data) { expect_only_attributes(Node, {"name", "capacity", "width", "height", "area"}, loc_data); /* Load type name */ auto Prop = get_attribute(Node, "name", loc_data).value(); PhysicalTileType->name = vtr::strdup(Prop); /* Load properties */ PhysicalTileType->capacity = get_attribute(Node, "capacity", loc_data, ReqOpt::OPTIONAL).as_uint(1); PhysicalTileType->width = get_attribute(Node, "width", loc_data, ReqOpt::OPTIONAL).as_uint(1); PhysicalTileType->height = get_attribute(Node, "height", loc_data, ReqOpt::OPTIONAL).as_uint(1); PhysicalTileType->area = get_attribute(Node, "area", loc_data, ReqOpt::OPTIONAL).as_float(UNDEFINED); if (atof(Prop) < 0) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Area for type %s must be non-negative\n", PhysicalTileType->name); } } static void ProcessTilePorts(pugi::xml_node Parent, t_physical_tile_type* PhysicalTileType, const pugiutil::loc_data& loc_data) { pugi::xml_node Cur; std::map tile_port_names; int num_ports, num_in_ports, num_out_ports, num_clock_ports; num_ports = num_in_ports = num_out_ports = num_clock_ports = 0; num_in_ports = count_children(Parent, "input", loc_data, ReqOpt::OPTIONAL); num_out_ports = count_children(Parent, "output", loc_data, ReqOpt::OPTIONAL); num_clock_ports = count_children(Parent, "clock", loc_data, ReqOpt::OPTIONAL); num_ports = num_in_ports + num_out_ports + num_clock_ports; int iport = 0; int k; int absolute_first_pin_index = 0; for (int itype = 0; itype < 3; itype++) { if (itype == 0) { k = 0; Cur = get_first_child(Parent, "input", loc_data, ReqOpt::OPTIONAL); } else if (itype == 1) { k = 0; Cur = get_first_child(Parent, "output", loc_data, ReqOpt::OPTIONAL); } else { k = 0; Cur = get_first_child(Parent, "clock", loc_data, ReqOpt::OPTIONAL); } while (Cur) { t_physical_tile_port port; port.index = iport; port.absolute_first_pin_index = absolute_first_pin_index; port.port_index_by_type = k; ProcessTilePort(Cur, &port, loc_data); absolute_first_pin_index += port.num_pins; //Check port name duplicates auto result = tile_port_names.insert(std::pair(port.name, 0)); if (!result.second) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Duplicate port names in tile '%s': port '%s'\n", PhysicalTileType->name, port.name); } //Push port PhysicalTileType->ports.push_back(port); /* get next iteration */ iport++; k++; Cur = Cur.next_sibling(Cur.name()); } } VTR_ASSERT(iport == num_ports); /* Count stats on the number of each type of pin */ for (const auto& port : PhysicalTileType->ports) { if (port.type == IN_PORT && port.is_clock == false) { PhysicalTileType->num_input_pins += port.num_pins; } else if (port.type == OUT_PORT) { PhysicalTileType->num_output_pins += port.num_pins; } else { VTR_ASSERT(port.is_clock && port.type == IN_PORT); PhysicalTileType->num_clock_pins += port.num_pins; } } } static void ProcessTilePort(pugi::xml_node Node, t_physical_tile_port* port, const pugiutil::loc_data& loc_data) { std::vector expected_attributes = {"name", "num_pins", "equivalent"}; if (Node.name() == "input"s || Node.name() == "clock"s) { expected_attributes.push_back("is_non_clock_global"); } expect_only_attributes(Node, expected_attributes, loc_data); const char* Prop; Prop = get_attribute(Node, "name", loc_data).value(); port->name = vtr::strdup(Prop); Prop = get_attribute(Node, "equivalent", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (Prop) { if (Prop == "none"s) { port->equivalent = PortEquivalence::NONE; } else if (Prop == "full"s) { port->equivalent = PortEquivalence::FULL; } else if (Prop == "instance"s) { if (Node.name() == "output"s) { port->equivalent = PortEquivalence::INSTANCE; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Invalid pin equivalence '%s' for %s port.", Prop, Node.name()); } } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Invalid pin equivalence '%s'.", Prop); } } port->num_pins = get_attribute(Node, "num_pins", loc_data).as_int(0); port->is_non_clock_global = get_attribute(Node, "is_non_clock_global", loc_data, ReqOpt::OPTIONAL) .as_bool(false); if (port->num_pins <= 0) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Invalid number of pins %d for %s port.", port->num_pins, Node.name()); } if (0 == strcmp(Node.name(), "input")) { port->type = IN_PORT; port->is_clock = false; } else if (0 == strcmp(Node.name(), "output")) { port->type = OUT_PORT; port->is_clock = false; } else if (0 == strcmp(Node.name(), "clock")) { port->type = IN_PORT; port->is_clock = true; if (port->is_non_clock_global == true) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Port %s cannot be both a clock and a non-clock simultaneously\n", Node.name()); } } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Unknown port type %s", Node.name()); } } static void ProcessTileEquivalentSites(pugi::xml_node Parent, t_physical_tile_type* PhysicalTileType, std::vector& LogicalBlockTypes, const pugiutil::loc_data& loc_data) { pugi::xml_node CurSite; expect_only_children(Parent, {"site"}, loc_data); if (count_children(Parent, "site", loc_data) < 1) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "There are no sites corresponding to this tile: %s.\n", PhysicalTileType->name); } CurSite = Parent.first_child(); while (CurSite) { check_node(CurSite, "site", loc_data); expect_only_attributes(CurSite, {"pb_type", "pin_mapping"}, loc_data); /* Load equivalent site name */ auto Prop = std::string(get_attribute(CurSite, "pb_type", loc_data).value()); auto LogicalBlockType = get_type_by_name(Prop.c_str(), LogicalBlockTypes); auto pin_mapping = get_attribute(CurSite, "pin_mapping", loc_data, ReqOpt::OPTIONAL).as_string("direct"); if (0 == strcmp(pin_mapping, "custom")) { // Pin mapping between Tile and Pb Type is user-defined ProcessEquivalentSiteCustomConnection(CurSite, PhysicalTileType, LogicalBlockType, Prop, loc_data); } else if (0 == strcmp(pin_mapping, "direct")) { ProcessEquivalentSiteDirectConnection(CurSite, PhysicalTileType, LogicalBlockType, loc_data); } if (0 == strcmp(LogicalBlockType->pb_type->name, Prop.c_str())) { PhysicalTileType->equivalent_sites.push_back(LogicalBlockType); check_port_direct_mappings(PhysicalTileType, LogicalBlockType); } CurSite = CurSite.next_sibling(CurSite.name()); } } static void ProcessEquivalentSiteDirectConnection(pugi::xml_node Parent, t_physical_tile_type* PhysicalTileType, t_logical_block_type* LogicalBlockType, const pugiutil::loc_data& loc_data) { int num_pins = PhysicalTileType->num_pins / PhysicalTileType->capacity; if (num_pins != LogicalBlockType->pb_type->num_pins) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Pin definition differ between site %s and tile %s. User-defined pin mapping is required.\n", LogicalBlockType->pb_type->name, PhysicalTileType->name); } vtr::bimap directs_map; for (int npin = 0; npin < num_pins; npin++) { t_physical_pin physical_pin(npin); t_logical_pin logical_pin(npin); directs_map.insert(logical_pin, physical_pin); } PhysicalTileType->tile_block_pin_directs_map[LogicalBlockType->index] = directs_map; } static void ProcessEquivalentSiteCustomConnection(pugi::xml_node Parent, t_physical_tile_type* PhysicalTileType, t_logical_block_type* LogicalBlockType, std::string site_name, const pugiutil::loc_data& loc_data) { pugi::xml_node CurDirect; expect_only_children(Parent, {"direct"}, loc_data); if (count_children(Parent, "direct", loc_data) < 1) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "There are no direct pin mappings between site %s and tile %s.\n", site_name.c_str(), PhysicalTileType->name); } vtr::bimap directs_map; CurDirect = Parent.first_child(); while (CurDirect) { check_node(CurDirect, "direct", loc_data); expect_only_attributes(CurDirect, {"from", "to"}, loc_data); std::string from, to; // `from` attribute is relative to the physical tile pins from = std::string(get_attribute(CurDirect, "from", loc_data).value()); // `to` attribute is relative to the logical block pins to = std::string(get_attribute(CurDirect, "to", loc_data).value()); auto from_pins = ProcessPinString(CurDirect, PhysicalTileType, from.c_str(), loc_data); auto to_pins = ProcessPinString(CurDirect, LogicalBlockType, to.c_str(), loc_data); // Checking that the number of pins is exactly the same if (from_pins.second - from_pins.first != to_pins.second - to_pins.first) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "The number of pins specified in the direct pin mapping is " "not equivalent for Physical Tile %s and Logical Block %s.\n", PhysicalTileType->name, LogicalBlockType->name); } int num_pins = from_pins.second - from_pins.first; for (int i = 0; i < num_pins; i++) { t_physical_pin physical_pin(from_pins.first + i); t_logical_pin logical_pin(to_pins.first + i); auto result = directs_map.insert(logical_pin, physical_pin); if (!result.second) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Parent), "Duplicate logical pin (%d) to physical pin (%d) mappings found for " "Physical Tile %s and Logical Block %s.\n", logical_pin.pin, physical_pin.pin, PhysicalTileType->name, LogicalBlockType->name); } } CurDirect = CurDirect.next_sibling(CurDirect.name()); } PhysicalTileType->tile_block_pin_directs_map[LogicalBlockType->index] = directs_map; } /* Takes in node pointing to and loads all the * child type objects. */ static void ProcessComplexBlocks(pugi::xml_node Node, std::vector& LogicalBlockTypes, t_arch& arch, const bool timing_enabled, const pugiutil::loc_data& loc_data) { pugi::xml_node CurBlockType; pugi::xml_node Cur; std::map pb_type_descriptors; /* Alloc the type list. Need one additional t_type_desctiptors: * 1: empty psuedo-type */ t_logical_block_type EMPTY_LOGICAL_BLOCK_TYPE = SetupEmptyLogicalType(); EMPTY_LOGICAL_BLOCK_TYPE.index = 0; LogicalBlockTypes.push_back(EMPTY_LOGICAL_BLOCK_TYPE); /* Process the types */ int index = 1; /* Skip over 'empty' type */ CurBlockType = Node.first_child(); while (CurBlockType) { check_node(CurBlockType, "pb_type", loc_data); t_logical_block_type LogicalBlockType; expect_only_attributes(CurBlockType, {"name"}, loc_data); /* Load type name */ auto Prop = get_attribute(CurBlockType, "name", loc_data).value(); LogicalBlockType.name = vtr::strdup(Prop); auto result = pb_type_descriptors.insert(std::pair(LogicalBlockType.name, 0)); if (!result.second) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(CurBlockType), "Duplicate pb_type descriptor name: '%s'.\n", LogicalBlockType.name); } /* Load pb_type info to assign to the Logical Block Type */ LogicalBlockType.pb_type = new t_pb_type; LogicalBlockType.pb_type->name = vtr::strdup(LogicalBlockType.name); ProcessPb_Type(CurBlockType, LogicalBlockType.pb_type, nullptr, timing_enabled, arch, loc_data); LogicalBlockType.index = index; /* Type fully read */ ++index; /* Push newly created Types to corresponding vectors */ LogicalBlockTypes.push_back(LogicalBlockType); /* Free this node and get its next sibling node */ CurBlockType = CurBlockType.next_sibling(CurBlockType.name()); } pb_type_descriptors.clear(); } static void ProcessSegments(pugi::xml_node Parent, std::vector& Segs, const t_arch_switch_inf* Switches, const int NumSwitches, const bool timing_enabled, const bool switchblocklist_required, const pugiutil::loc_data& loc_data) { int i, j, length; const char* tmp; pugi::xml_node SubElem; pugi::xml_node Node; /* Count the number of segs and check they are in fact * of segment elements. */ int NumSegs = count_children(Parent, "segment", loc_data); /* Alloc segment list */ if (NumSegs > 0) { Segs.resize(NumSegs); } /* Load the segments. */ Node = get_first_child(Parent, "segment", loc_data); for (i = 0; i < NumSegs; ++i) { /* Get segment name */ tmp = get_attribute(Node, "name", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (tmp) { Segs[i].name = std::string(tmp); } else { /* if swich block is "custom", then you have to provide a name for segment */ if (switchblocklist_required) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "No name specified for the segment #%d.\n", i); } /* set name to default: "unnamed_segment_" */ std::stringstream ss; ss << "unnamed_segment_" << i; std::string dummy = ss.str(); tmp = dummy.c_str(); Segs[i].name = std::string(tmp); } /* Get segment length */ length = 1; /* DEFAULT */ tmp = get_attribute(Node, "length", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (tmp) { if (strcmp(tmp, "longline") == 0) { Segs[i].longline = true; } else { length = vtr::atoi(tmp); } } Segs[i].length = length; /* Get the frequency */ Segs[i].frequency = 1; /* DEFAULT */ tmp = get_attribute(Node, "freq", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (tmp) { Segs[i].frequency = (int)(atof(tmp) * MAX_CHANNEL_WIDTH); } /* Get timing info */ ReqOpt TIMING_ENABLE_REQD = BoolToReqOpt(timing_enabled); Segs[i].Rmetal = get_attribute(Node, "Rmetal", loc_data, TIMING_ENABLE_REQD).as_float(0); Segs[i].Cmetal = get_attribute(Node, "Cmetal", loc_data, TIMING_ENABLE_REQD).as_float(0); /* Get Power info */ /* * (*Segs)[i].Cmetal_per_m = get_attribute(Node, "Cmetal_per_m", false, * 0.);*/ //Set of expected subtags (exact subtags are dependant on parameters) std::vector expected_subtags; if (!Segs[i].longline) { //Long line doesn't accpet or since it assumes full population expected_subtags.push_back("sb"); expected_subtags.push_back("cb"); } /* Get the type */ tmp = get_attribute(Node, "type", loc_data).value(); if (0 == strcmp(tmp, "bidir")) { Segs[i].directionality = BI_DIRECTIONAL; //Bidir requires the following tags expected_subtags.push_back("wire_switch"); expected_subtags.push_back("opin_switch"); } else if (0 == strcmp(tmp, "unidir")) { Segs[i].directionality = UNI_DIRECTIONAL; //Unidir requires the following tags expected_subtags.push_back("mux"); } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Invalid switch type '%s'.\n", tmp); } //Verify only expected sub-tags are found expect_only_children(Node, expected_subtags, loc_data); /* Get the wire and opin switches, or mux switch if unidir */ if (UNI_DIRECTIONAL == Segs[i].directionality) { SubElem = get_single_child(Node, "mux", loc_data); tmp = get_attribute(SubElem, "name", loc_data).value(); /* Match names */ for (j = 0; j < NumSwitches; ++j) { if (0 == strcmp(tmp, Switches[j].name)) { break; /* End loop so j is where we want it */ } } if (j >= NumSwitches) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(SubElem), "'%s' is not a valid mux name.\n", tmp); } /* Unidir muxes must have the same switch * for wire and opin fanin since there is * really only the mux in unidir. */ Segs[i].arch_wire_switch = j; Segs[i].arch_opin_switch = j; } else { VTR_ASSERT(BI_DIRECTIONAL == Segs[i].directionality); SubElem = get_single_child(Node, "wire_switch", loc_data); tmp = get_attribute(SubElem, "name", loc_data).value(); /* Match names */ for (j = 0; j < NumSwitches; ++j) { if (0 == strcmp(tmp, Switches[j].name)) { break; /* End loop so j is where we want it */ } } if (j >= NumSwitches) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(SubElem), "'%s' is not a valid wire_switch name.\n", tmp); } Segs[i].arch_wire_switch = j; SubElem = get_single_child(Node, "opin_switch", loc_data); tmp = get_attribute(SubElem, "name", loc_data).value(); /* Match names */ for (j = 0; j < NumSwitches; ++j) { if (0 == strcmp(tmp, Switches[j].name)) { break; /* End loop so j is where we want it */ } } if (j >= NumSwitches) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(SubElem), "'%s' is not a valid opin_switch name.\n", tmp); } Segs[i].arch_opin_switch = j; } /* Setup the CB list if they give one, otherwise use full */ Segs[i].cb.resize(length); for (j = 0; j < length; ++j) { Segs[i].cb[j] = true; } SubElem = get_single_child(Node, "cb", loc_data, ReqOpt::OPTIONAL); if (SubElem) { ProcessCB_SB(SubElem, Segs[i].cb, loc_data); } /* Setup the SB list if they give one, otherwise use full */ Segs[i].sb.resize(length + 1); for (j = 0; j < (length + 1); ++j) { Segs[i].sb[j] = true; } SubElem = get_single_child(Node, "sb", loc_data, ReqOpt::OPTIONAL); if (SubElem) { ProcessCB_SB(SubElem, Segs[i].sb, loc_data); } /* Get next Node */ Node = Node.next_sibling(Node.name()); } } /* Processes the switchblocklist section from the xml architecture file. * See vpr/SRC/route/build_switchblocks.c for a detailed description of this * switch block format */ static void ProcessSwitchblocks(pugi::xml_node Parent, t_arch* arch, const pugiutil::loc_data& loc_data) { pugi::xml_node Node; pugi::xml_node SubElem; const char* tmp; /* get the number of switchblocks */ int num_switchblocks = count_children(Parent, "switchblock", loc_data); arch->switchblocks.reserve(num_switchblocks); /* read-in all switchblock data */ Node = get_first_child(Parent, "switchblock", loc_data); for (int i_sb = 0; i_sb < num_switchblocks; i_sb++) { /* use a temp variable which will be assigned to switchblocks later */ t_switchblock_inf sb; /* get name */ tmp = get_attribute(Node, "name", loc_data).as_string(nullptr); if (tmp) { sb.name = tmp; } /* get type */ tmp = get_attribute(Node, "type", loc_data).as_string(nullptr); if (tmp) { if (0 == strcmp(tmp, "bidir")) { sb.directionality = BI_DIRECTIONAL; } else if (0 == strcmp(tmp, "unidir")) { sb.directionality = UNI_DIRECTIONAL; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Unsopported switchblock type: %s\n", tmp); } } /* get the switchblock location */ SubElem = get_single_child(Node, "switchblock_location", loc_data); tmp = get_attribute(SubElem, "type", loc_data).as_string(nullptr); if (tmp) { if (strcmp(tmp, "EVERYWHERE") == 0) { sb.location = E_EVERYWHERE; } else if (strcmp(tmp, "PERIMETER") == 0) { sb.location = E_PERIMETER; } else if (strcmp(tmp, "CORE") == 0) { sb.location = E_CORE; } else if (strcmp(tmp, "CORNER") == 0) { sb.location = E_CORNER; } else if (strcmp(tmp, "FRINGE") == 0) { sb.location = E_FRINGE; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(SubElem), "unrecognized switchblock location: %s\n", tmp); } } /* get switchblock permutation functions */ SubElem = get_first_child(Node, "switchfuncs", loc_data); read_sb_switchfuncs(SubElem, &sb, loc_data); read_sb_wireconns(arch->Switches, arch->num_switches, Node, &sb, loc_data); /* run error checks on switch blocks */ check_switchblock(&sb, arch); /* assign the sb to the switchblocks vector */ arch->switchblocks.push_back(sb); Node = Node.next_sibling(Node.name()); } return; } static void ProcessCB_SB(pugi::xml_node Node, std::vector& list, const pugiutil::loc_data& loc_data) { const char* tmp = nullptr; int i; int len = list.size(); /* Check the type. We only support 'pattern' for now. * Should add frac back eventually. */ tmp = get_attribute(Node, "type", loc_data).value(); if (0 == strcmp(tmp, "pattern")) { i = 0; /* Get the content string */ tmp = Node.child_value(); while (*tmp) { switch (*tmp) { case ' ': case '\t': case '\n': break; case 'T': case '1': if (i >= len) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "CB or SB depopulation is too long (%d). It should be %d symbols for CBs and %d symbols for SBs.\n", i, len - 1, len); } list[i] = true; ++i; break; case 'F': case '0': if (i >= len) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "CB or SB depopulation is too long (%d). It should be %d symbols for CBs and %d symbols for SBs.\n", i, len - 1, len); } list[i] = false; ++i; break; default: archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Invalid character %c in CB or SB depopulation list.\n", *tmp); } ++tmp; } if (i < len) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "CB or SB depopulation is too short (%d). It should be %d symbols for CBs and %d symbols for SBs.\n", i, len - 1, len); } } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "'%s' is not a valid type for specifying cb and sb depopulation.\n", tmp); } } static void ProcessSwitches(pugi::xml_node Parent, t_arch_switch_inf** Switches, int* NumSwitches, const bool timing_enabled, const pugiutil::loc_data& loc_data) { int i, j; const char* type_name; const char* switch_name; ReqOpt TIMING_ENABLE_REQD = BoolToReqOpt(timing_enabled); pugi::xml_node Node; /* Count the children and check they are switches */ *NumSwitches = count_children(Parent, "switch", loc_data); /* Alloc switch list */ *Switches = nullptr; if (*NumSwitches > 0) { (*Switches) = new t_arch_switch_inf[(*NumSwitches)]; } /* Load the switches. */ Node = get_first_child(Parent, "switch", loc_data); for (i = 0; i < *NumSwitches; ++i) { t_arch_switch_inf& arch_switch = (*Switches)[i]; switch_name = get_attribute(Node, "name", loc_data).value(); type_name = get_attribute(Node, "type", loc_data).value(); /* Check for switch name collisions */ for (j = 0; j < i; ++j) { if (0 == strcmp((*Switches)[j].name, switch_name)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Two switches with the same name '%s' were found.\n", switch_name); } } arch_switch.name = vtr::strdup(switch_name); /* Figure out the type of switch */ /* As noted above, due to their configuration of pass transistors feeding into a buffer, * only multiplexers and tristate buffers have an internal capacitance element. */ SwitchType type = SwitchType::MUX; if (0 == strcmp(type_name, "mux")) { type = SwitchType::MUX; expect_only_attributes(Node, {"type", "name", "R", "Cin", "Cout", "Cinternal", "Tdel", "buf_size", "power_buf_size", "mux_trans_size"}, " with type '"s + type_name + "'"s, loc_data); } else if (0 == strcmp(type_name, "tristate")) { type = SwitchType::TRISTATE; expect_only_attributes(Node, {"type", "name", "R", "Cin", "Cout", "Cinternal", "Tdel", "buf_size", "power_buf_size"}, " with type '"s + type_name + "'"s, loc_data); } else if (0 == strcmp(type_name, "buffer")) { type = SwitchType::BUFFER; expect_only_attributes(Node, {"type", "name", "R", "Cin", "Cout", "Tdel", "buf_size", "power_buf_size"}, " with type '"s + type_name + "'"s, loc_data); } else if (0 == strcmp(type_name, "pass_gate")) { type = SwitchType::PASS_GATE; expect_only_attributes(Node, {"type", "name", "R", "Cin", "Cout", "Tdel"}, " with type '"s + type_name + "'"s, loc_data); } else if (0 == strcmp(type_name, "short")) { type = SwitchType::SHORT; expect_only_attributes(Node, {"type", "name", "R", "Cin", "Cout", "Tdel"}, " with type "s + type_name + "'"s, loc_data); } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Invalid switch type '%s'.\n", type_name); } arch_switch.set_type(type); arch_switch.R = get_attribute(Node, "R", loc_data, TIMING_ENABLE_REQD).as_float(0); ReqOpt COUT_REQD = TIMING_ENABLE_REQD; ReqOpt CIN_REQD = TIMING_ENABLE_REQD; // We have defined the Cinternal parameter as optional, so that the user may specify an // architecture without Cinternal without breaking the program flow. ReqOpt CINTERNAL_REQD = ReqOpt::OPTIONAL; if (arch_switch.type() == SwitchType::SHORT) { //Cin/Cout are optional on shorts, since they really only have one capacitance CIN_REQD = ReqOpt::OPTIONAL; COUT_REQD = ReqOpt::OPTIONAL; } arch_switch.Cin = get_attribute(Node, "Cin", loc_data, CIN_REQD).as_float(0); arch_switch.Cout = get_attribute(Node, "Cout", loc_data, COUT_REQD).as_float(0); arch_switch.Cinternal = get_attribute(Node, "Cinternal", loc_data, CINTERNAL_REQD).as_float(0); if (arch_switch.type() == SwitchType::MUX) { //Only muxes have mux transistors arch_switch.mux_trans_size = get_attribute(Node, "mux_trans_size", loc_data, ReqOpt::OPTIONAL).as_float(1); } else { arch_switch.mux_trans_size = 0.; } if (arch_switch.type() == SwitchType::SHORT || arch_switch.type() == SwitchType::PASS_GATE) { //No buffers arch_switch.buf_size_type = BufferSize::ABSOLUTE; arch_switch.buf_size = 0.; arch_switch.power_buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; arch_switch.power_buffer_size = 0.; } else { auto buf_size_attrib = get_attribute(Node, "buf_size", loc_data, ReqOpt::OPTIONAL); if (!buf_size_attrib || buf_size_attrib.as_string() == std::string("auto")) { arch_switch.buf_size_type = BufferSize::AUTO; arch_switch.buf_size = 0.; } else { arch_switch.buf_size_type = BufferSize::ABSOLUTE; arch_switch.buf_size = buf_size_attrib.as_float(); } auto power_buf_size = get_attribute(Node, "power_buf_size", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (power_buf_size == nullptr) { arch_switch.power_buffer_type = POWER_BUFFER_TYPE_AUTO; } else if (strcmp(power_buf_size, "auto") == 0) { arch_switch.power_buffer_type = POWER_BUFFER_TYPE_AUTO; } else { arch_switch.power_buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; arch_switch.power_buffer_size = (float)vtr::atof(power_buf_size); } } //Load the Tdel (which may be specfied with sub-tags) ProcessSwitchTdel(Node, timing_enabled, i, (*Switches), loc_data); /* Get next switch element */ Node = Node.next_sibling(Node.name()); } } /* Processes the switch delay. Switch delay can be specified in two ways. * First way: switch delay is specified as a constant via the property Tdel in the switch node. * Second way: switch delay is specified as a function of the switch fan-in. In this * case, multiple nodes in the form * * * * are specified as children of the switch node. In this case, Tdel * is not included as a property of the switch node (first way). */ static void ProcessSwitchTdel(pugi::xml_node Node, const bool timing_enabled, const int switch_index, t_arch_switch_inf* Switches, const pugiutil::loc_data& loc_data) { float Tdel_prop_value; int num_Tdel_children; /* check if switch node has the Tdel property */ bool has_Tdel_prop = false; Tdel_prop_value = get_attribute(Node, "Tdel", loc_data, ReqOpt::OPTIONAL).as_float(UNDEFINED); if (Tdel_prop_value != UNDEFINED) { has_Tdel_prop = true; } /* check if switch node has Tdel children */ bool has_Tdel_children = false; num_Tdel_children = count_children(Node, "Tdel", loc_data, ReqOpt::OPTIONAL); if (num_Tdel_children != 0) { has_Tdel_children = true; } /* delay should not be specified as a Tdel property AND a Tdel child */ if (has_Tdel_prop && has_Tdel_children) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Switch delay should be specified as EITHER a Tdel property OR as a child of the switch node, not both"); } /* get pointer to the switch's Tdel map, then read-in delay data into this map */ if (has_Tdel_prop) { /* delay specified as a constant */ Switches[switch_index].set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, Tdel_prop_value); } else if (has_Tdel_children) { /* Delay specified as a function of switch fan-in. * Go through each Tdel child, read-in num_inputs and the delay value. * Insert this info into the switch delay map */ pugi::xml_node Tdel_child = get_first_child(Node, "Tdel", loc_data); std::set seen_fanins; for (int ichild = 0; ichild < num_Tdel_children; ichild++) { int num_inputs = get_attribute(Tdel_child, "num_inputs", loc_data).as_int(0); float Tdel_value = get_attribute(Tdel_child, "delay", loc_data).as_float(0.); if (seen_fanins.count(num_inputs)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Tdel_child), "Tdel node specified num_inputs (%d) that has already been specified by another Tdel node", num_inputs); } else { Switches[switch_index].set_Tdel(num_inputs, Tdel_value); seen_fanins.insert(num_inputs); } Tdel_child = Tdel_child.next_sibling(Tdel_child.name()); } } else { /* No delay info specified for switch */ if (timing_enabled) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Switch should contain intrinsic delay information if timing is enabled"); } else { /* set a default value */ Switches[switch_index].set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, 0.); } } } static void ProcessDirects(pugi::xml_node Parent, t_direct_inf** Directs, int* NumDirects, const t_arch_switch_inf* Switches, const int NumSwitches, const pugiutil::loc_data& loc_data) { int i, j; const char* direct_name; const char* from_pin_name; const char* to_pin_name; const char* switch_name; pugi::xml_node Node; /* Count the children and check they are direct connections */ expect_only_children(Parent, {"direct"}, loc_data); *NumDirects = count_children(Parent, "direct", loc_data); /* Alloc direct list */ *Directs = nullptr; if (*NumDirects > 0) { *Directs = (t_direct_inf*)vtr::malloc(*NumDirects * sizeof(t_direct_inf)); memset(*Directs, 0, (*NumDirects * sizeof(t_direct_inf))); } /* Load the directs. */ Node = get_first_child(Parent, "direct", loc_data); for (i = 0; i < *NumDirects; ++i) { expect_only_attributes(Node, {"name", "from_pin", "to_pin", "x_offset", "y_offset", "z_offset", "switch_name", "from_side", "to_side"}, loc_data); direct_name = get_attribute(Node, "name", loc_data).value(); /* Check for direct name collisions */ for (j = 0; j < i; ++j) { if (0 == strcmp((*Directs)[j].name, direct_name)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Two directs with the same name '%s' were found.\n", direct_name); } } (*Directs)[i].name = vtr::strdup(direct_name); /* Figure out the source pin and sink pin name */ from_pin_name = get_attribute(Node, "from_pin", loc_data).value(); to_pin_name = get_attribute(Node, "to_pin", loc_data).value(); /* Check that to_pin and the from_pin are not the same */ if (0 == strcmp(to_pin_name, from_pin_name)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "The source pin and sink pin are the same: %s.\n", to_pin_name); } (*Directs)[i].from_pin = vtr::strdup(from_pin_name); (*Directs)[i].to_pin = vtr::strdup(to_pin_name); (*Directs)[i].x_offset = get_attribute(Node, "x_offset", loc_data).as_int(0); (*Directs)[i].y_offset = get_attribute(Node, "y_offset", loc_data).as_int(0); (*Directs)[i].z_offset = get_attribute(Node, "z_offset", loc_data).as_int(0); std::string from_side_str = get_attribute(Node, "from_side", loc_data, ReqOpt::OPTIONAL).value(); (*Directs)[i].from_side = string_to_side(from_side_str); std::string to_side_str = get_attribute(Node, "to_side", loc_data, ReqOpt::OPTIONAL).value(); (*Directs)[i].to_side = string_to_side(to_side_str); //Set the optional switch type switch_name = get_attribute(Node, "switch_name", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (switch_name != nullptr) { //Look-up the user defined switch for (j = 0; j < NumSwitches; j++) { if (0 == strcmp(switch_name, Switches[j].name)) { break; //Found the switch } } if (j >= NumSwitches) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Could not find switch named '%s' in switch list.\n", switch_name); } (*Directs)[i].switch_type = j; //Save the correct switch index } else { //If not defined, use the delayless switch by default //TODO: find a better way of indicating this. Ideally, we would //specify the delayless switch index here, but it does not appear //to be defined at this point. (*Directs)[i].switch_type = -1; } /* Check that the direct chain connection is not zero in both direction */ if ((*Directs)[i].x_offset == 0 && (*Directs)[i].y_offset == 0) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "The x_offset and y_offset are both zero, this is a length 0 direct chain connection.\n"); } (*Directs)[i].line = loc_data.line(Node); /* Should I check that the direct chain offset is not greater than the chip? How? */ /* Get next direct element */ Node = Node.next_sibling(Node.name()); } } static void ProcessClockMetalLayers(pugi::xml_node parent, std::unordered_map& metal_layers, pugiutil::loc_data& loc_data) { std::vector expected_attributes = {"name", "Rmetal", "Cmetal"}; std::vector expected_children = {"metal_layer"}; pugi::xml_node metal_layers_parent = get_single_child(parent, "metal_layers", loc_data); int num_metal_layers = count_children(metal_layers_parent, "metal_layer", loc_data); pugi::xml_node curr_layer = get_first_child(metal_layers_parent, "metal_layer", loc_data); for (int i = 0; i < num_metal_layers; i++) { expect_only_children(metal_layers_parent, expected_children, loc_data); expect_only_attributes(curr_layer, expected_attributes, loc_data); // Get metal layer values: name, r_metal, and c_metal std::string name(get_attribute(curr_layer, "name", loc_data).value()); t_metal_layer metal_layer; metal_layer.r_metal = get_attribute(curr_layer, "Rmetal", loc_data).as_float(0.); metal_layer.c_metal = get_attribute(curr_layer, "Cmetal", loc_data).as_float(0.); // Insert metal layer into map auto itter = metal_layers.find(name); if (itter != metal_layers.end()) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(curr_layer), "Two metal layers with the same name '%s' were found.\n", name.c_str()); } metal_layers.insert({name, metal_layer}); curr_layer = curr_layer.next_sibling(curr_layer.name()); } } static void ProcessClockNetworks(pugi::xml_node parent, std::vector& clock_networks, const t_arch_switch_inf* switches, const int num_switches, pugiutil::loc_data& loc_data) { std::vector expected_spine_attributes = {"name", "num_inst", "metal_layer", "starty", "endy", "x", "repeatx", "repeaty"}; std::vector expected_rib_attributes = {"name", "num_inst", "metal_layer", "startx", "endx", "y", "repeatx", "repeaty"}; std::vector expected_children = {"rib", "spine"}; int num_clock_networks = count_children(parent, "clock_network", loc_data); pugi::xml_node curr_network = get_first_child(parent, "clock_network", loc_data); for (int i = 0; i < num_clock_networks; i++) { expect_only_children(curr_network, expected_children, loc_data); t_clock_network_arch clock_network; std::string name(get_attribute(curr_network, "name", loc_data).value()); clock_network.name = name; clock_network.num_inst = get_attribute(curr_network, "num_inst", loc_data).as_int(0); bool is_supported_clock_type = false; pugi::xml_node curr_type; // Parse spine curr_type = get_single_child(curr_network, "spine", loc_data, ReqOpt::OPTIONAL); if (curr_type) { expect_only_attributes(curr_network, expected_spine_attributes, loc_data); is_supported_clock_type = true; clock_network.type = e_clock_type::SPINE; std::string metal_layer(get_attribute(curr_type, "metal_layer", loc_data).value()); std::string starty(get_attribute(curr_type, "starty", loc_data).value()); std::string endy(get_attribute(curr_type, "endy", loc_data).value()); std::string x(get_attribute(curr_type, "x", loc_data).value()); std::string repeatx; auto repeatx_attr = get_attribute(curr_type, "repeatx", loc_data, ReqOpt::OPTIONAL); if (repeatx_attr) { repeatx = repeatx_attr.value(); } else { repeatx = "W"; } std::string repeaty; auto repeaty_attr = get_attribute(curr_type, "repeaty", loc_data, ReqOpt::OPTIONAL); if (repeaty_attr) { repeaty = repeaty_attr.value(); } else { repeaty = "H"; } clock_network.metal_layer = metal_layer; clock_network.wire.start = starty; clock_network.wire.end = endy; clock_network.wire.position = x; clock_network.repeat.x = repeatx; clock_network.repeat.y = repeaty; ProcessClockSwitchPoints(curr_type, clock_network, switches, num_switches, loc_data); } // Parse rib curr_type = get_single_child(curr_network, "rib", loc_data, ReqOpt::OPTIONAL); if (curr_type) { expect_only_attributes(curr_network, expected_spine_attributes, loc_data); is_supported_clock_type = true; clock_network.type = e_clock_type::RIB; std::string metal_layer(get_attribute(curr_type, "metal_layer", loc_data).value()); std::string startx(get_attribute(curr_type, "startx", loc_data).value()); std::string endx(get_attribute(curr_type, "endx", loc_data).value()); std::string y(get_attribute(curr_type, "y", loc_data).value()); std::string repeatx; auto repeatx_attr = get_attribute(curr_type, "repeatx", loc_data, ReqOpt::OPTIONAL); if (repeatx_attr) { repeatx = repeatx_attr.value(); } else { repeatx = "W"; } std::string repeaty; auto repeaty_attr = get_attribute(curr_type, "repeaty", loc_data, ReqOpt::OPTIONAL); if (repeaty_attr) { repeaty = repeaty_attr.value(); } else { repeaty = "H"; } clock_network.metal_layer = metal_layer; clock_network.wire.start = startx; clock_network.wire.end = endx; clock_network.wire.position = y; clock_network.repeat.x = repeatx; clock_network.repeat.y = repeaty; ProcessClockSwitchPoints(curr_type, clock_network, switches, num_switches, loc_data); } // Currently their is only support for ribs and spines if (!is_supported_clock_type) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(curr_type), "Found no supported clock network type for '%s' clock network.\n" "Currently there is only support for rib and spine networks.\n", name.c_str()); } clock_networks.push_back(clock_network); curr_network = curr_network.next_sibling(curr_network.name()); } } static void ProcessClockSwitchPoints(pugi::xml_node parent, t_clock_network_arch& clock_network, const t_arch_switch_inf* switches, const int num_switches, pugiutil::loc_data& loc_data) { std::vector expected_spine_drive_attributes = {"name", "type", "yoffset", "switch_name"}; std::vector expected_rib_drive_attributes = {"name", "type", "xoffset", "switch_name"}; std::vector expected_spine_tap_attributes = {"name", "type", "yoffset", "yincr"}; std::vector expected_rib_tap_attributes = {"name", "type", "xoffset", "xincr"}; std::vector expected_children = {"switch_point"}; int num_clock_switches = count_children(parent, "switch_point", loc_data); pugi::xml_node curr_switch = get_first_child(parent, "switch_point", loc_data); //TODO: currently only supporting one drive and one tap. Should change to support // multiple taps VTR_ASSERT(num_switches != 2); //TODO: ensure switch name is unique for every switch of this clock network for (int i = 0; i < num_clock_switches; i++) { expect_only_children(curr_switch, expected_children, loc_data); std::string switch_type(get_attribute(curr_switch, "type", loc_data).value()); if (switch_type == "drive") { t_clock_drive drive; std::string name(get_attribute(curr_switch, "name", loc_data).value()); const char* offset; if (clock_network.type == e_clock_type::SPINE) { expect_only_attributes(curr_switch, expected_spine_drive_attributes, loc_data); offset = get_attribute(curr_switch, "yoffset", loc_data).value(); } else { VTR_ASSERT(clock_network.type == e_clock_type::RIB); expect_only_attributes(curr_switch, expected_rib_drive_attributes, loc_data); offset = get_attribute(curr_switch, "xoffset", loc_data).value(); } // get switch index const char* switch_name = get_attribute(curr_switch, "switch_name", loc_data).value(); int switch_idx; for (switch_idx = 0; switch_idx < num_switches; switch_idx++) { if (0 == strcmp(switch_name, switches[switch_idx].name)) { break; // switch_idx has been found } } if (switch_idx >= num_switches) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(curr_switch), "'%s' is not a valid switch name.\n", switch_name); } drive.name = name; drive.offset = offset; drive.arch_switch_idx = switch_idx; clock_network.drive = drive; } else if (switch_type == "tap") { t_clock_taps tap; std::string name(get_attribute(curr_switch, "name", loc_data).value()); const char* offset; const char* increment; if (clock_network.type == e_clock_type::SPINE) { expect_only_attributes(curr_switch, expected_spine_tap_attributes, loc_data); offset = get_attribute(curr_switch, "yoffset", loc_data).value(); increment = get_attribute(curr_switch, "yincr", loc_data).value(); } else { VTR_ASSERT(clock_network.type == e_clock_type::RIB); expect_only_attributes(curr_switch, expected_rib_tap_attributes, loc_data); offset = get_attribute(curr_switch, "xoffset", loc_data).value(); increment = get_attribute(curr_switch, "xincr", loc_data).value(); } tap.name = name; tap.offset = offset; tap.increment = increment; clock_network.tap = tap; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(curr_switch), "Found unsupported switch type for '%s' clock network.\n" "Currently there is only support for drive and tap switch types.\n", clock_network.name.c_str()); } curr_switch = curr_switch.next_sibling(curr_switch.name()); } } static void ProcessClockRouting(pugi::xml_node parent, std::vector& clock_connections, const t_arch_switch_inf* switches, const int num_switches, pugiutil::loc_data& loc_data) { std::vector expected_attributes = {"from", "to", "switch", "fc_val", "locationx", "locationy"}; pugi::xml_node clock_routing_parent = get_single_child(parent, "clock_routing", loc_data); int num_routing_connections = count_children(clock_routing_parent, "tap", loc_data); pugi::xml_node curr_connection = get_first_child(clock_routing_parent, "tap", loc_data); for (int i = 0; i < num_routing_connections; i++) { expect_only_attributes(curr_connection, expected_attributes, loc_data); t_clock_connection_arch clock_connection; const char* from = get_attribute(curr_connection, "from", loc_data).value(); const char* to = get_attribute(curr_connection, "to", loc_data).value(); const char* switch_name = get_attribute(curr_connection, "switch", loc_data).value(); const char* locationx = get_attribute(curr_connection, "locationx", loc_data, ReqOpt::OPTIONAL).value(); const char* locationy = get_attribute(curr_connection, "locationy", loc_data, ReqOpt::OPTIONAL).value(); float fc = get_attribute(curr_connection, "fc_val", loc_data).as_float(0.); int switch_idx; for (switch_idx = 0; switch_idx < num_switches; switch_idx++) { if (0 == strcmp(switch_name, switches[switch_idx].name)) { break; // switch_idx has been found } } if (switch_idx >= num_switches) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(curr_connection), "'%s' is not a valid switch name.\n", switch_name); } clock_connection.from = from; clock_connection.to = to; clock_connection.arch_switch_idx = switch_idx; clock_connection.locationx = locationx; clock_connection.locationy = locationy; clock_connection.fc = fc; clock_connections.push_back(clock_connection); curr_connection = curr_connection.next_sibling(curr_connection.name()); } } static void ProcessPower(pugi::xml_node parent, t_power_arch* power_arch, const pugiutil::loc_data& loc_data) { pugi::xml_node Cur; /* Get the local interconnect capacitances */ power_arch->local_interc_factor = 0.5; Cur = get_single_child(parent, "local_interconnect", loc_data, ReqOpt::OPTIONAL); if (Cur) { power_arch->C_wire_local = get_attribute(Cur, "C_wire", loc_data, ReqOpt::OPTIONAL).as_float(0.); power_arch->local_interc_factor = get_attribute(Cur, "factor", loc_data, ReqOpt::OPTIONAL).as_float(0.5); } /* Get logical effort factor */ power_arch->logical_effort_factor = 4.0; Cur = get_single_child(parent, "buffers", loc_data, ReqOpt::OPTIONAL); if (Cur) { power_arch->logical_effort_factor = get_attribute(Cur, "logical_effort_factor", loc_data) .as_float(0); ; } /* Get SRAM Size */ power_arch->transistors_per_SRAM_bit = 6.0; Cur = get_single_child(parent, "sram", loc_data, ReqOpt::OPTIONAL); if (Cur) { power_arch->transistors_per_SRAM_bit = get_attribute(Cur, "transistors_per_bit", loc_data) .as_float(0); } /* Get Mux transistor size */ power_arch->mux_transistor_size = 1.0; Cur = get_single_child(parent, "mux_transistor_size", loc_data, ReqOpt::OPTIONAL); if (Cur) { power_arch->mux_transistor_size = get_attribute(Cur, "mux_transistor_size", loc_data) .as_float(0); } /* Get FF size */ power_arch->FF_size = 1.0; Cur = get_single_child(parent, "FF_size", loc_data, ReqOpt::OPTIONAL); if (Cur) { power_arch->FF_size = get_attribute(Cur, "FF_size", loc_data).as_float(0); } /* Get LUT transistor size */ power_arch->LUT_transistor_size = 1.0; Cur = get_single_child(parent, "LUT_transistor_size", loc_data, ReqOpt::OPTIONAL); if (Cur) { power_arch->LUT_transistor_size = get_attribute(Cur, "LUT_transistor_size", loc_data) .as_float(0); } } /* Get the clock architcture */ static void ProcessClocks(pugi::xml_node Parent, t_clock_arch* clocks, const pugiutil::loc_data& loc_data) { pugi::xml_node Node; int i; const char* tmp; clocks->num_global_clocks = count_children(Parent, "clock", loc_data, ReqOpt::OPTIONAL); /* Alloc the clockdetails */ clocks->clock_inf = nullptr; if (clocks->num_global_clocks > 0) { clocks->clock_inf = (t_clock_network*)vtr::malloc(clocks->num_global_clocks * sizeof(t_clock_network)); memset(clocks->clock_inf, 0, clocks->num_global_clocks * sizeof(t_clock_network)); } /* Load the clock info. */ Node = get_first_child(Parent, "clock", loc_data); for (i = 0; i < clocks->num_global_clocks; ++i) { tmp = get_attribute(Node, "buffer_size", loc_data).value(); if (strcmp(tmp, "auto") == 0) { clocks->clock_inf[i].autosize_buffer = true; } else { clocks->clock_inf[i].autosize_buffer = false; clocks->clock_inf[i].buffer_size = (float)atof(tmp); } clocks->clock_inf[i].C_wire = get_attribute(Node, "C_wire", loc_data).as_float(0); /* get the next clock item */ Node = Node.next_sibling(Node.name()); } } /* Used by functions outside read_xml_util.c to gain access to arch filename */ const char* get_arch_file_name() { return arch_file_name; } bool check_model_clocks(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model) { //Collect the ports identified as clocks std::set clocks; for (t_model_ports* ports : {model->inputs, model->outputs}) { for (t_model_ports* port = ports; port != nullptr; port = port->next) { if (port->is_clock) { clocks.insert(port->name); } } } //Check that any clock references on the ports are to identified clock ports for (t_model_ports* ports : {model->inputs, model->outputs}) { for (t_model_ports* port = ports; port != nullptr; port = port->next) { if (!port->clock.empty() && !clocks.count(port->clock)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(model_tag), "No matching clock port '%s' on model '%s', required for port '%s'", port->clock.c_str(), model->name, port->name); } } } return true; } bool check_model_combinational_sinks(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model) { //Outputs should have no combinational sinks for (t_model_ports* port = model->outputs; port != nullptr; port = port->next) { if (port->combinational_sink_ports.size() != 0) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(model_tag), "Model '%s' output port '%s' can not have combinational sink ports", model->name, port->name); } } //Record the output ports std::set output_ports; for (t_model_ports* port = model->outputs; port != nullptr; port = port->next) { output_ports.insert(port->name); } //Check that the input port combinational sinks are all outputs for (t_model_ports* port = model->inputs; port != nullptr; port = port->next) { for (const std::string& sink_port_name : port->combinational_sink_ports) { if (!output_ports.count(sink_port_name)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(model_tag), "Model '%s' input port '%s' can not be combinationally connected to '%s' (not an output port of the model)", model->name, port->name, sink_port_name.c_str()); } } } return true; } void warn_model_missing_timing(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model) { //Check whether there are missing edges and warn the user std::set comb_connected_outputs; for (t_model_ports* port = model->inputs; port != nullptr; port = port->next) { if (port->clock.empty() //Not sequential && port->combinational_sink_ports.empty() //Doesn't drive any combinational outputs && !port->is_clock //Not an input clock ) { VTR_LOGF_WARN(loc_data.filename_c_str(), loc_data.line(model_tag), "Model '%s' input port '%s' has no timing specification (no clock specified to create a sequential input port, not combinationally connected to any outputs, not a clock input)\n", model->name, port->name); } comb_connected_outputs.insert(port->combinational_sink_ports.begin(), port->combinational_sink_ports.end()); } for (t_model_ports* port = model->outputs; port != nullptr; port = port->next) { if (port->clock.empty() //Not sequential && !comb_connected_outputs.count(port->name) //Not combinationally drivven && !port->is_clock //Not an output clock ) { VTR_LOGF_WARN(loc_data.filename_c_str(), loc_data.line(model_tag), "Model '%s' output port '%s' has no timing specification (no clock specified to create a sequential output port, not combinationally connected to any inputs, not a clock output)\n", model->name, port->name); } } } bool check_leaf_pb_model_timing_consistency(const t_pb_type* pb_type, const t_arch& arch) { //Normalize the blif model name to match the model name // by removing the leading '.' (.latch, .inputs, .names etc.) // by removing the leading '.subckt' VTR_ASSERT(pb_type->blif_model); std::string blif_model = pb_type->blif_model; std::string subckt = ".subckt "; auto pos = blif_model.find(subckt); if (pos != std::string::npos) { blif_model = blif_model.substr(pos + subckt.size()); } //Find the matching model const t_model* model = nullptr; for (const t_model* models : {arch.models, arch.model_library}) { for (model = models; model != nullptr; model = model->next) { if (std::string(model->name) == blif_model) { break; } } if (model != nullptr) { break; } } if (model == nullptr) { archfpga_throw(get_arch_file_name(), -1, "Unable to find model for blif_model '%s' found on pb_type '%s'", blif_model.c_str(), pb_type->name); } //Now that we have the model we can compare the timing annotations //Check from the pb_type's delay annotations match the model // // This ensures that the pb_types' delay annotations are consistent with the model for (int i = 0; i < pb_type->num_annotations; ++i) { const t_pin_to_pin_annotation* annot = &pb_type->annotations[i]; if (annot->type == E_ANNOT_PIN_TO_PIN_DELAY) { //Check that any combinational delays specified match the 'combinational_sinks_ports' in the model if (annot->clock) { //Sequential annotation, check that the clock on the specified port matches the model //Annotations always put the pin in the input_pins field VTR_ASSERT(annot->input_pins); for (const std::string& input_pin : vtr::split(annot->input_pins)) { InstPort annot_port(input_pin); for (const std::string& clock : vtr::split(annot->clock)) { InstPort annot_clock(clock); //Find the model port const t_model_ports* model_port = nullptr; for (const t_model_ports* ports : {model->inputs, model->outputs}) { for (const t_model_ports* port = ports; port != nullptr; port = port->next) { if (port->name == annot_port.port_name()) { model_port = port; break; } } if (model_port != nullptr) break; } if (model_port == nullptr) { archfpga_throw(get_arch_file_name(), annot->line_num, "Failed to find port '%s' on '%s' for sequential delay annotation", annot_port.port_name().c_str(), annot_port.instance_name().c_str()); } //Check that the clock matches the model definition std::string model_clock = model_port->clock; if (model_clock.empty()) { archfpga_throw(get_arch_file_name(), annot->line_num, " timing-annotation/ mismatch on port '%s' of model '%s', model specifies" " no clock but timing annotation specifies '%s'", annot_port.port_name().c_str(), model->name, annot_clock.port_name().c_str()); } if (model_port->clock != annot_clock.port_name()) { archfpga_throw(get_arch_file_name(), annot->line_num, " timing-annotation/ mismatch on port '%s' of model '%s', model specifies" " clock as '%s' but timing annotation specifies '%s'", annot_port.port_name().c_str(), model->name, model_clock.c_str(), annot_clock.port_name().c_str()); } } } } else if (annot->input_pins && annot->output_pins) { //Combinational annotation VTR_ASSERT_MSG(!annot->clock, "Combinational annotations should have no clock"); for (const std::string& input_pin : vtr::split(annot->input_pins)) { InstPort annot_in(input_pin); for (const std::string& output_pin : vtr::split(annot->output_pins)) { InstPort annot_out(output_pin); //Find the input model port const t_model_ports* model_port = nullptr; for (const t_model_ports* port = model->inputs; port != nullptr; port = port->next) { if (port->name == annot_in.port_name()) { model_port = port; break; } } if (model_port == nullptr) { archfpga_throw(get_arch_file_name(), annot->line_num, "Failed to find port '%s' on '%s' for combinational delay annotation", annot_in.port_name().c_str(), annot_in.instance_name().c_str()); } //Check that the output port is listed in the model's combinational sinks auto b = model_port->combinational_sink_ports.begin(); auto e = model_port->combinational_sink_ports.end(); auto iter = std::find(b, e, annot_out.port_name()); if (iter == e) { archfpga_throw(get_arch_file_name(), annot->line_num, " timing-annotation/ mismatch on port '%s' of model '%s', timing annotation" " specifies combinational connection to port '%s' but the connection does not exist in the model", model_port->name, model->name, annot_out.port_name().c_str()); } } } } else { throw ArchFpgaError("Unrecognized delay annotation"); } } } //Build a list of combinationally connected sinks std::set comb_connected_outputs; for (t_model_ports* model_ports : {model->inputs, model->outputs}) { for (t_model_ports* model_port = model_ports; model_port != nullptr; model_port = model_port->next) { comb_connected_outputs.insert(model_port->combinational_sink_ports.begin(), model_port->combinational_sink_ports.end()); } } //Check from the model to pb_type's delay annotations // // This ensures that the pb_type has annotations for all delays/values // required by the model for (t_model_ports* model_ports : {model->inputs, model->outputs}) { for (t_model_ports* model_port = model_ports; model_port != nullptr; model_port = model_port->next) { //If the model port has no timing specification don't check anything (e.g. architectures with no timing info) if (model_port->clock.empty() && model_port->combinational_sink_ports.empty() && !comb_connected_outputs.count(model_port->name)) { continue; } if (!model_port->clock.empty()) { //Sequential port if (model_port->dir == IN_PORT) { //Sequential inputs must have a T_setup or T_hold if (find_sequential_annotation(pb_type, model_port, E_ANNOT_PIN_TO_PIN_DELAY_TSETUP) == nullptr && find_sequential_annotation(pb_type, model_port, E_ANNOT_PIN_TO_PIN_DELAY_THOLD) == nullptr) { std::stringstream msg; msg << " '" << pb_type->name << "' timing-annotation/ mismatch on"; msg << " port '" << model_port->name << "' of model '" << model->name << "',"; msg << " port is a sequential input but has neither T_setup nor T_hold specified"; if (is_library_model(model)) { //Only warn if timing info is missing from a library model (e.g. .names/.latch on a non-timing architecture) VTR_LOGF_WARN(get_arch_file_name(), -1, "%s\n", msg.str().c_str()); } else { archfpga_throw(get_arch_file_name(), -1, msg.str().c_str()); } } if (!model_port->combinational_sink_ports.empty()) { //Sequential input with internal combinational connectsion it must also have T_clock_to_Q if (find_sequential_annotation(pb_type, model_port, E_ANNOT_PIN_TO_PIN_DELAY_CLOCK_TO_Q_MAX) == nullptr && find_sequential_annotation(pb_type, model_port, E_ANNOT_PIN_TO_PIN_DELAY_CLOCK_TO_Q_MIN) == nullptr) { std::stringstream msg; msg << " '" << pb_type->name << "' timing-annotation/ mismatch on"; msg << " port '" << model_port->name << "' of model '" << model->name << "',"; msg << " port is a sequential input with internal combinational connects but has neither"; msg << " min nor max T_clock_to_Q specified"; if (is_library_model(model)) { //Only warn if timing info is missing from a library model (e.g. .names/.latch on a non-timing architecture) VTR_LOGF_WARN(get_arch_file_name(), -1, "%s\n", msg.str().c_str()); } else { archfpga_throw(get_arch_file_name(), -1, msg.str().c_str()); } } } } else { VTR_ASSERT(model_port->dir == OUT_PORT); //Sequential outputs must have T_clock_to_Q if (find_sequential_annotation(pb_type, model_port, E_ANNOT_PIN_TO_PIN_DELAY_CLOCK_TO_Q_MAX) == nullptr && find_sequential_annotation(pb_type, model_port, E_ANNOT_PIN_TO_PIN_DELAY_CLOCK_TO_Q_MIN) == nullptr) { std::stringstream msg; msg << " '" << pb_type->name << "' timing-annotation/ mismatch on"; msg << " port '" << model_port->name << "' of model '" << model->name << "',"; msg << " port is a sequential output but has neither min nor max T_clock_to_Q specified"; if (is_library_model(model)) { //Only warn if timing info is missing from a library model (e.g. .names/.latch on a non-timing architecture) VTR_LOGF_WARN(get_arch_file_name(), -1, "%s\n", msg.str().c_str()); } else { archfpga_throw(get_arch_file_name(), -1, msg.str().c_str()); } } if (comb_connected_outputs.count(model_port->name)) { //Sequential output with internal combinational connectison must have T_setup/T_hold if (find_sequential_annotation(pb_type, model_port, E_ANNOT_PIN_TO_PIN_DELAY_TSETUP) == nullptr && find_sequential_annotation(pb_type, model_port, E_ANNOT_PIN_TO_PIN_DELAY_THOLD) == nullptr) { std::stringstream msg; msg << " '" << pb_type->name << "' timing-annotation/ mismatch on"; msg << " port '" << model_port->name << "' of model '" << model->name << "',"; msg << " port is a sequential output with internal combinational connections but has"; msg << " neither T_setup nor T_hold specified"; if (is_library_model(model)) { //Only warn if timing info is missing from a library model (e.g. .names/.latch on a non-timing architecture) VTR_LOGF_WARN(get_arch_file_name(), -1, "%s\n", msg.str().c_str()); } else { archfpga_throw(get_arch_file_name(), -1, msg.str().c_str()); } } } } } //Check that combinationally connected inputs/outputs have combinational delays between them if (model_port->dir == IN_PORT) { for (const auto& sink_port : model_port->combinational_sink_ports) { if (find_combinational_annotation(pb_type, model_port->name, sink_port) == nullptr) { std::stringstream msg; msg << " '" << pb_type->name << "' timing-annotation/ mismatch on"; msg << " port '" << model_port->name << "' of model '" << model->name << "',"; msg << " input port '" << model_port->name << "' has combinational connections to"; msg << " port '" << sink_port.c_str() << "'; specified in model, but no combinational delays found on pb_type"; if (is_library_model(model)) { //Only warn if timing info is missing from a library model (e.g. .names/.latch on a non-timing architecture) VTR_LOGF_WARN(get_arch_file_name(), -1, "%s\n", msg.str().c_str()); } else { archfpga_throw(get_arch_file_name(), -1, msg.str().c_str()); } } } } } } return true; } 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) { VTR_ASSERT(annot_type == E_ANNOT_PIN_TO_PIN_DELAY_TSETUP || annot_type == E_ANNOT_PIN_TO_PIN_DELAY_THOLD || annot_type == E_ANNOT_PIN_TO_PIN_DELAY_CLOCK_TO_Q_MAX || annot_type == E_ANNOT_PIN_TO_PIN_DELAY_CLOCK_TO_Q_MIN); for (int iannot = 0; iannot < pb_type->num_annotations; ++iannot) { const t_pin_to_pin_annotation* annot = &pb_type->annotations[iannot]; InstPort annot_in(annot->input_pins); if (annot_in.port_name() == port->name) { for (int iprop = 0; iprop < annot->num_value_prop_pairs; ++iprop) { if (annot->prop[iprop] == annot_type) { return annot; } } } } return nullptr; } const t_pin_to_pin_annotation* find_combinational_annotation(const t_pb_type* pb_type, std::string in_port, std::string out_port) { for (int iannot = 0; iannot < pb_type->num_annotations; ++iannot) { const t_pin_to_pin_annotation* annot = &pb_type->annotations[iannot]; for (const auto& annot_in_str : vtr::split(annot->input_pins)) { InstPort in_pins(annot_in_str); for (const auto& annot_out_str : vtr::split(annot->output_pins)) { InstPort out_pins(annot_out_str); if (in_pins.port_name() == in_port && out_pins.port_name() == out_port) { for (int iprop = 0; iprop < annot->num_value_prop_pairs; ++iprop) { if (annot->prop[iprop] == E_ANNOT_PIN_TO_PIN_DELAY_MAX || annot->prop[iprop] == E_ANNOT_PIN_TO_PIN_DELAY_MIN) { return annot; } } } } } } return nullptr; } std::string inst_port_to_port_name(std::string inst_port) { auto pos = inst_port.find('.'); if (pos != std::string::npos) { return inst_port.substr(pos + 1); } return inst_port; } static bool attribute_to_bool(const pugi::xml_node node, const pugi::xml_attribute attr, const pugiutil::loc_data& loc_data) { if (attr.value() == std::string("1")) { return true; } else if (attr.value() == std::string("0")) { return false; } else { bad_attribute_value(attr, node, loc_data, {"0", "1"}); } return false; } int find_switch_by_name(const t_arch& arch, std::string switch_name) { for (int iswitch = 0; iswitch < arch.num_switches; ++iswitch) { const t_arch_switch_inf& arch_switch = arch.Switches[iswitch]; if (arch_switch.name == switch_name) { return iswitch; } } return OPEN; } e_side string_to_side(std::string side_str) { e_side side = NUM_SIDES; if (side_str.empty()) { side = NUM_SIDES; } else if (side_str == "left") { side = LEFT; } else if (side_str == "right") { side = RIGHT; } else if (side_str == "top") { side = TOP; } else if (side_str == "bottom") { side = BOTTOM; } else { archfpga_throw(__FILE__, __LINE__, "Invalid side specification"); } 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& equivalent_sites = physical_tile.equivalent_sites; auto criteria = [physical_tile](const t_logical_block_type* lhs, const t_logical_block_type* rhs) { int num_physical_pins = physical_tile.num_pins / physical_tile.capacity; 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_physical_pins - lhs_num_logical_pins; int rhs_diff_num_pins = num_physical_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_physical_pins = lhs->num_pins / lhs->capacity; int rhs_num_physical_pins = rhs->num_pins / rhs->capacity; int lhs_diff_num_pins = lhs_num_physical_pins - num_logical_pins; int rhs_diff_num_pins = rhs_num_physical_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 : logical_block.equivalent_tiles) { auto direct_map = tile->tile_block_pin_directs_map.at(logical_block.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 phy_index = result->second.pin; 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 sites\n.", tile->name, phy_index, pin); } 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); } } } } } static void check_port_direct_mappings(t_physical_tile_type_ptr physical_tile, t_logical_block_type_ptr logical_block) { auto pb_type = logical_block->pb_type; if (pb_type->num_pins > physical_tile->num_pins) { archfpga_throw(__FILE__, __LINE__, "Logical Block (%s) has more pins than the Physical Tile (%s).\n", logical_block->name, physical_tile->name); } auto& pin_direct_mapping = physical_tile->tile_block_pin_directs_map.at(logical_block->index); if (pb_type->num_pins != (int)pin_direct_mapping.size()) { archfpga_throw(__FILE__, __LINE__, "Logical block (%s) and Physical tile (%s) have a different number of ports.\n", logical_block->name, physical_tile->name); } for (auto pin_map : pin_direct_mapping) { auto block_port = get_port_by_pin(logical_block, pin_map.first.pin); auto tile_port = get_port_by_pin(physical_tile, pin_map.second.pin); VTR_ASSERT(block_port != nullptr); VTR_ASSERT(tile_port != nullptr); if (tile_port->type != block_port->type || tile_port->num_pins != block_port->num_pins || tile_port->equivalent != block_port->equivalent) { archfpga_throw(__FILE__, __LINE__, "Logical block (%s) and Physical tile (%s) do not have equivalent port specifications.\n", logical_block->name, physical_tile->name); } } } static const t_physical_tile_port* get_port_by_name(t_physical_tile_type_ptr type, const char* port_name) { for (auto port : type->ports) { if (0 == strcmp(port.name, port_name)) { return &type->ports[port.index]; } } return nullptr; } static const t_port* get_port_by_name(t_logical_block_type_ptr type, const char* port_name) { auto pb_type = type->pb_type; for (int i = 0; i < pb_type->num_ports; i++) { auto port = pb_type->ports[i]; if (0 == strcmp(port.name, port_name)) { return &pb_type->ports[port.index]; } } return nullptr; } static const t_physical_tile_port* get_port_by_pin(t_physical_tile_type_ptr type, int pin) { for (auto port : type->ports) { if (pin >= port.absolute_first_pin_index && pin < port.absolute_first_pin_index + port.num_pins) { return &type->ports[port.index]; } } return nullptr; } static const t_port* get_port_by_pin(t_logical_block_type_ptr type, int pin) { auto pb_type = type->pb_type; for (int i = 0; i < pb_type->num_ports; i++) { auto port = pb_type->ports[i]; if (pin >= port.absolute_first_pin_index && pin < port.absolute_first_pin_index + port.num_pins) { return &pb_type->ports[port.index]; } } return nullptr; } 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); }