diff --git a/libopenfpga/libarchopenfpga/arch/sample_arch.xml b/libopenfpga/libarchopenfpga/arch/sample_arch.xml index dafaa83dd..1d9f8f5a0 100644 --- a/libopenfpga/libarchopenfpga/arch/sample_arch.xml +++ b/libopenfpga/libarchopenfpga/arch/sample_arch.xml @@ -266,7 +266,7 @@ - + diff --git a/libopenfpga/libarchopenfpga/src/arch_direct.cpp b/libopenfpga/libarchopenfpga/src/arch_direct.cpp new file mode 100644 index 000000000..a741dd878 --- /dev/null +++ b/libopenfpga/libarchopenfpga/src/arch_direct.cpp @@ -0,0 +1,112 @@ +#include "vtr_assert.h" + +#include "arch_direct.h" + +/************************************************************************ + * Member functions for class ArchDirect + ***********************************************************************/ + +/************************************************************************ + * Constructors + ***********************************************************************/ +ArchDirect::ArchDirect() { + return; +} + +/************************************************************************ + * Public Accessors : aggregates + ***********************************************************************/ +ArchDirect::arch_direct_range ArchDirect::directs() const { + return vtr::make_range(direct_ids_.begin(), direct_ids_.end()); +} + +/************************************************************************ + * Public Accessors + ***********************************************************************/ +ArchDirectId ArchDirect::direct(const std::string& name) const { + if (0 < direct_name2ids_.count(name)) { + return direct_name2ids_.at(name); + } + return ArchDirectId::INVALID(); +} + +std::string ArchDirect::name(const ArchDirectId& direct_id) const { + /* validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return names_[direct_id]; +} + +CircuitModelId ArchDirect::circuit_model(const ArchDirectId& direct_id) const { + /* validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return circuit_models_[direct_id]; +} + +e_direct_type ArchDirect::type(const ArchDirectId& direct_id) const { + /* validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return types_[direct_id]; +} + +e_direct_direction ArchDirect::x_dir(const ArchDirectId& direct_id) const { + /* validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return directions_[direct_id].x(); +} + +e_direct_direction ArchDirect::y_dir(const ArchDirectId& direct_id) const { + /* validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return directions_[direct_id].y(); +} + +/************************************************************************ + * Public Mutators + ***********************************************************************/ +ArchDirectId ArchDirect::add_direct(const std::string& name) { + if (0 < direct_name2ids_.count(name)) { + return ArchDirectId::INVALID(); + } + + /* This is a legal name. we can create a new id */ + ArchDirectId direct = ArchDirectId(direct_ids_.size()); + direct_ids_.push_back(direct); + names_.push_back(name); + circuit_models_.push_back(CircuitModelId::INVALID()); + types_.emplace_back(NUM_DIRECT_TYPES); + directions_.emplace_back(vtr::Point(NUM_DIRECT_DIRECTIONS, NUM_DIRECT_DIRECTIONS)); + + /* Register in the name-to-id map */ + direct_name2ids_[name] = direct; + + return direct; +} + +void ArchDirect::set_circuit_model(const ArchDirectId& direct_id, const CircuitModelId& circuit_model) { + /* validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + circuit_models_[direct_id] = circuit_model; +} + +void ArchDirect::set_type(const ArchDirectId& direct_id, const e_direct_type& type) { + /* validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + types_[direct_id] = type; +} + +void ArchDirect::set_direction(const ArchDirectId& direct_id, + const e_direct_direction& x_dir, + const e_direct_direction& y_dir) { + /* validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + directions_[direct_id].set_x(x_dir); + directions_[direct_id].set_y(y_dir); +} + +/************************************************************************ + * Internal invalidators/validators + ***********************************************************************/ +/* Validators */ +bool ArchDirect::valid_direct_id(const ArchDirectId& direct_id) const { + return ( size_t(direct_id) < direct_ids_.size() ) && ( direct_id == direct_ids_[direct_id] ); +} diff --git a/libopenfpga/libarchopenfpga/src/arch_direct.h b/libopenfpga/libarchopenfpga/src/arch_direct.h new file mode 100644 index 000000000..067b257d1 --- /dev/null +++ b/libopenfpga/libarchopenfpga/src/arch_direct.h @@ -0,0 +1,103 @@ +#ifndef ARCH_DIRECT_H +#define ARCH_DIRECT_H + +#include + +#include "vtr_vector.h" +#include "vtr_geometry.h" + +#include "circuit_library_fwd.h" +#include "arch_direct_fwd.h" + +/******************************************************************** + * Define the types of point to point connection between CLBs + * These types are supplementary to the original VPR direct connections + * Here we extend to the cross-row and cross-column connections + ********************************************************************/ +enum e_direct_type { + INNER_COLUMN, + INNER_ROW, + INTER_COLUMN, + INTER_ROW, + NUM_DIRECT_TYPES +}; +constexpr std::array DIRECT_TYPE_STRING = {{"inner_column", "inner_row", "inter_column", "inter_row"}}; + +enum e_direct_direction { + POSITIVE_DIR, + NEGATIVE_DIR, + NUM_DIRECT_DIRECTIONS +}; +constexpr std::array DIRECT_DIRECTION_STRING = {{"positive", "negative"}}; + +/******************************************************************** + * A data base to describe the direct connection in OpenFPGA architecture + * For each direct connection, it will include + * - name: the identifier to annotate the original direct connection in VPR architecture + * - circuit model: the circuit model to used to implement this connection + * - type: if this connection should be cross-column or cross-row + * - x-direction: how this connection is going to be applied to adjacent columns + * a positive x-direction means that column A will be connected + * to the column B on the right side of column A + * - y-direction: how this connection is going to be applied to adjacent rows + * a positive y-direction means that row A will be connected + * to the row B on the bottom side of row A + * + * Note that: this is the data structure to be used when parsing the XML + * this is NOT the data structure to be use in core engine + ********************************************************************/ +class ArchDirect { + public: /* Types */ + typedef vtr::vector::const_iterator arch_direct_iterator; + /* Create range */ + typedef vtr::Range arch_direct_range; + + public: /* Constructors */ + ArchDirect(); + + public: /* Accessors: aggregates */ + arch_direct_range directs() const; + + public: /* Public Accessors: Basic data query on directs */ + ArchDirectId direct(const std::string& name) const; + std::string name(const ArchDirectId& direct_id) const; + CircuitModelId circuit_model(const ArchDirectId& direct_id) const; + e_direct_type type(const ArchDirectId& direct_id) const; + e_direct_direction x_dir(const ArchDirectId& direct_id) const; + e_direct_direction y_dir(const ArchDirectId& direct_id) const; + public: /* Public Mutators */ + ArchDirectId add_direct(const std::string& name); + void set_circuit_model(const ArchDirectId& direct_id, const CircuitModelId& circuit_model); + void set_type(const ArchDirectId& direct_id, const e_direct_type& type); + void set_direction(const ArchDirectId& direct_id, + const e_direct_direction& x_dir, + const e_direct_direction& y_dir); + public: /* Public invalidators/validators */ + bool valid_direct_id(const ArchDirectId& direct_id) const; + private: /* Internal data */ + vtr::vector direct_ids_; + + /* Unique name: the identifier to annotate the original direct connection in VPR architecture */ + vtr::vector names_; + + /* circuit model: the circuit model to used to implement this connection */ + vtr::vector circuit_models_; + + /* type: if this connection should be cross-column or cross-row */ + vtr::vector types_; + + /* + * x-direction: how this connection is going to be applied to adjacent columns + * a positive x-direction means that column A will be connected + * to the column B on the right side of column A + * y-direction: how this connection is going to be applied to adjacent rows + * a positive y-direction means that row A will be connected + * to the row B on the bottom side of row A + */ + vtr::vector> directions_; + + /* Fast look-up */ + std::map direct_name2ids_; +}; + +#endif diff --git a/libopenfpga/libarchopenfpga/src/arch_direct_fwd.h b/libopenfpga/libarchopenfpga/src/arch_direct_fwd.h new file mode 100644 index 000000000..8a1f302f3 --- /dev/null +++ b/libopenfpga/libarchopenfpga/src/arch_direct_fwd.h @@ -0,0 +1,22 @@ +/************************************************************************ + * A header file for ArchDirect class, including critical data declaration + * Please include this file only for using any TechnologyLibrary data structure + * Refer to arch_direct.h for more details + ***********************************************************************/ + +/************************************************************************ + * Create strong id for ArchDirect to avoid illegal type casting + ***********************************************************************/ +#ifndef ARCH_DIRECT_FWD_H +#define ARCH_DIRECT_FWD_H + +#include "vtr_strong_id.h" + +struct arch_direct_id_tag; + +typedef vtr::StrongId ArchDirectId; + +/* Short declaration of class */ +class ArchDirect; + +#endif diff --git a/libopenfpga/libarchopenfpga/src/direct_types.h b/libopenfpga/libarchopenfpga/src/direct_types.h deleted file mode 100644 index 6b44f1fa5..000000000 --- a/libopenfpga/libarchopenfpga/src/direct_types.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef DIRECT_TYPES_H -#define DIRECT_TYPES_H - -/******************************************************************** - * Define the types of point to point connection between CLBs - * These types are supplementary to the original VPR direct connections - * Here we extend to the cross-row and cross-column connections - ********************************************************************/ -enum e_point2point_interconnection_type { - NO_P2P, - P2P_DIRECT_COLUMN, - P2P_DIRECT_ROW, - NUM_POINT2POINT_INTERCONNECT_TYPE -}; - -enum e_point2point_interconnection_dir { - POSITIVE_DIR, - NEGATIVE_DIR, - NUM_POINT2POINT_INTERCONNECT_DIR -}; - -#endif diff --git a/libopenfpga/libarchopenfpga/src/openfpga_arch.h b/libopenfpga/libarchopenfpga/src/openfpga_arch.h index 7f102dc85..a3dd71a12 100644 --- a/libopenfpga/libarchopenfpga/src/openfpga_arch.h +++ b/libopenfpga/libarchopenfpga/src/openfpga_arch.h @@ -8,6 +8,7 @@ #include "technology_library.h" #include "simulation_setting.h" #include "config_protocol.h" +#include "arch_direct.h" #include "pb_type_annotation.h" /* namespace openfpga begins */ @@ -48,7 +49,7 @@ struct Arch { /* Mapping from the names of direct connection * to circuit models in circuit library */ - std::map direct2circuit; + ArchDirect arch_direct; /* Pb type annotations * Bind from operating to physical diff --git a/libopenfpga/libarchopenfpga/src/read_xml_openfpga_arch.cpp b/libopenfpga/libarchopenfpga/src/read_xml_openfpga_arch.cpp index b35955f97..4c773f87f 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_openfpga_arch.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_openfpga_arch.cpp @@ -94,8 +94,8 @@ openfpga::Arch read_xml_openfpga_arch(const char* arch_file_name) { openfpga_arch.circuit_lib); /* Parse the direct circuit definition */ - openfpga_arch.direct2circuit = read_xml_direct_circuit(xml_openfpga_arch, loc_data, - openfpga_arch.circuit_lib); + openfpga_arch.arch_direct = read_xml_direct_circuit(xml_openfpga_arch, loc_data, + openfpga_arch.circuit_lib); /* Parse the pb_type annotation */ openfpga_arch.pb_type_annotations = read_xml_pb_type_annotations(xml_openfpga_arch, loc_data); diff --git a/libopenfpga/libarchopenfpga/src/read_xml_routing_circuit.cpp b/libopenfpga/libarchopenfpga/src/read_xml_routing_circuit.cpp index 060e26f1b..3114276ae 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_routing_circuit.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_routing_circuit.cpp @@ -185,20 +185,53 @@ std::map read_xml_routing_segment_circuit(pugi::xml return seg2circuit; } +/******************************************************************** + * Convert string to the enumerate of direct type + *******************************************************************/ +static +e_direct_type string_to_direct_type(const std::string& type_string) { + if (std::string("column") == type_string) { + return INTER_COLUMN; + } + + if (std::string("row") == type_string) { + return INTER_ROW; + } + + return NUM_DIRECT_TYPES; +} + +/******************************************************************** + * Convert string to the enumerate of direct direction type + *******************************************************************/ +static +e_direct_direction string_to_direct_direction(const std::string& type_string) { + if (std::string("positive") == type_string) { + return POSITIVE_DIR; + } + + if (std::string("negative") == type_string) { + return NEGATIVE_DIR; + } + + return NUM_DIRECT_DIRECTIONS; +} + + /******************************************************************** * Parse XML codes about to an object of name-to-circuit mapping * Note: this function should be called AFTER the parsing of circuit library!!! *******************************************************************/ -std::map read_xml_direct_circuit(pugi::xml_node& Node, - const pugiutil::loc_data& loc_data, - const CircuitLibrary& circuit_lib) { - std::map direct2circuit; +ArchDirect read_xml_direct_circuit(pugi::xml_node& Node, + const pugiutil::loc_data& loc_data, + const CircuitLibrary& circuit_lib) { + ArchDirect arch_direct; /* Parse direct list, this is optional. May not be used */ pugi::xml_node xml_directs= get_single_child(Node, "direct_connection", loc_data, pugiutil::ReqOpt::OPTIONAL); /* Not found, we can return */ if (!xml_directs) { - return direct2circuit; + return arch_direct; } /* Iterate over the children under this node, @@ -219,17 +252,46 @@ std::map read_xml_direct_circuit(pugi::xml_node& No circuit_lib, direct_model_name, CIRCUIT_MODEL_WIRE); - /* Ensure that there is no duplicated seg names defined here */ - std::map::const_iterator it = direct2circuit.find(direct_name); - if (it != direct2circuit.end()) { + /* Add to the Arch direct database */ + ArchDirectId direct = arch_direct.add_direct(direct_name); + if (false == arch_direct.valid_direct_id(direct)) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_direct), "Direct name '%s' has been defined more than once!\n", direct_name.c_str()); } + arch_direct.set_circuit_model(direct, direct_model); - /* Pass all the check, we can add it to the map */ - direct2circuit[direct_name] = direct_model; + /* Add more information*/ + std::string direct_type_name = get_attribute(xml_direct, "type", loc_data).as_string(); + e_direct_type direct_type = string_to_direct_type(direct_type_name); + + if (NUM_DIRECT_TYPES == direct_type) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_direct), + "Direct type '%s' is not support! Acceptable values are [column|row]\n", + direct_type_name.c_str()); + } + + arch_direct.set_type(direct, direct_type); + + std::string x_dir_name = get_attribute(xml_direct, "x_dir", loc_data).as_string(); + std::string y_dir_name = get_attribute(xml_direct, "y_dir", loc_data).as_string(); + e_direct_direction x_dir = string_to_direct_direction(x_dir_name); + e_direct_direction y_dir = string_to_direct_direction(y_dir_name); + + if (NUM_DIRECT_DIRECTIONS == x_dir) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_direct), + "Direct x-direction '%s' is not support! Acceptable values are [positive|column]\n", + x_dir_name.c_str()); + } + + if (NUM_DIRECT_DIRECTIONS == y_dir) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_direct), + "Direct y-direction '%s' is not support! Acceptable values are [positive|column]\n", + y_dir_name.c_str()); + } + + arch_direct.set_direction(direct, x_dir, y_dir); } - return direct2circuit; + return arch_direct; } diff --git a/libopenfpga/libarchopenfpga/src/read_xml_routing_circuit.h b/libopenfpga/libarchopenfpga/src/read_xml_routing_circuit.h index e00627022..28d14710d 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_routing_circuit.h +++ b/libopenfpga/libarchopenfpga/src/read_xml_routing_circuit.h @@ -10,6 +10,7 @@ #include "pugixml_util.hpp" #include "pugixml.hpp" #include "circuit_library.h" +#include "arch_direct.h" /******************************************************************** * Function declaration @@ -26,8 +27,9 @@ std::map read_xml_routing_segment_circuit(pugi::xml const pugiutil::loc_data& loc_data, const CircuitLibrary& circuit_lib); -std::map read_xml_direct_circuit(pugi::xml_node& Node, - const pugiutil::loc_data& loc_data, - const CircuitLibrary& circuit_lib); +ArchDirect read_xml_direct_circuit(pugi::xml_node& Node, + const pugiutil::loc_data& loc_data, + const CircuitLibrary& circuit_lib); + #endif diff --git a/libopenfpga/libarchopenfpga/src/write_xml_openfpga_arch.cpp b/libopenfpga/libarchopenfpga/src/write_xml_openfpga_arch.cpp index eb999dcd1..6d0a8ff06 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_openfpga_arch.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_openfpga_arch.cpp @@ -56,8 +56,7 @@ void write_xml_openfpga_arch(const char* fname, write_xml_routing_segment_circuit(fp, fname, openfpga_arch.circuit_lib, openfpga_arch.routing_seg2circuit); /* Write the direct connection circuit definition */ - write_xml_direct_circuit(fp, fname, openfpga_arch.circuit_lib, openfpga_arch.direct2circuit); - + write_xml_direct_circuit(fp, fname, openfpga_arch.circuit_lib, openfpga_arch.arch_direct); /* Write the pb_type annotations */ openfpga::write_xml_pb_type_annotations(fp, fname, openfpga_arch. pb_type_annotations); diff --git a/libopenfpga/libarchopenfpga/src/write_xml_routing_circuit.cpp b/libopenfpga/libarchopenfpga/src/write_xml_routing_circuit.cpp index 70f95f37f..978d38516 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_routing_circuit.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_routing_circuit.cpp @@ -38,6 +38,29 @@ void write_xml_routing_component_circuit(std::fstream& fp, } } +/******************************************************************** + * Write switch circuit model definition in XML format + *******************************************************************/ +static +void write_xml_direct_component_circuit(std::fstream& fp, + const char* fname, + const std::string& direct_tag_name, + const CircuitLibrary& circuit_lib, + const ArchDirect& arch_direct, + const ArchDirectId& direct_id) { + /* Validate the file stream */ + openfpga::check_file_stream(fname, fp); + + /* Iterate over the mapping */ + fp << "\t\t" << "<" << direct_tag_name; + write_xml_attribute(fp, "name", arch_direct.name(direct_id).c_str()); + write_xml_attribute(fp, "circuit_model_name", circuit_lib.model_name(arch_direct.circuit_model(direct_id)).c_str()); + write_xml_attribute(fp, "type", DIRECT_TYPE_STRING[arch_direct.type(direct_id)]); + write_xml_attribute(fp, "x_dir", DIRECT_DIRECTION_STRING[arch_direct.x_dir(direct_id)]); + write_xml_attribute(fp, "y_dir", DIRECT_DIRECTION_STRING[arch_direct.y_dir(direct_id)]); + fp << "/>" << "\n"; +} + /******************************************************************** * Write Connection block circuit models in XML format *******************************************************************/ @@ -104,9 +127,9 @@ void write_xml_routing_segment_circuit(std::fstream& fp, void write_xml_direct_circuit(std::fstream& fp, const char* fname, const CircuitLibrary& circuit_lib, - const std::map& direct2circuit) { + const ArchDirect& arch_direct) { /* If the direct2circuit is empty, we do not output XML */ - if (direct2circuit.empty()) { + if (0 == arch_direct.directs().size()) { return; } @@ -117,7 +140,9 @@ void write_xml_direct_circuit(std::fstream& fp, fp << "\t" << "" << "\n"; /* Write each direct connection circuit definition */ - write_xml_routing_component_circuit(fp, fname, std::string("direct"), circuit_lib, direct2circuit); + for (const ArchDirectId& direct_id : arch_direct.directs()) { + write_xml_direct_component_circuit(fp, fname, std::string("direct"), circuit_lib, arch_direct, direct_id); + } /* Finish writing the root node */ fp << "\t" << "" << "\n"; diff --git a/libopenfpga/libarchopenfpga/src/write_xml_routing_circuit.h b/libopenfpga/libarchopenfpga/src/write_xml_routing_circuit.h index 7cf2cd8ae..bddb7cc56 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_routing_circuit.h +++ b/libopenfpga/libarchopenfpga/src/write_xml_routing_circuit.h @@ -9,6 +9,7 @@ #include #include "circuit_library.h" +#include "arch_direct.h" /******************************************************************** * Function declaration @@ -31,6 +32,6 @@ void write_xml_routing_segment_circuit(std::fstream& fp, void write_xml_direct_circuit(std::fstream& fp, const char* fname, const CircuitLibrary& circuit_lib, - const std::map& direct2circuit); + const ArchDirect& arch_direct); #endif diff --git a/openfpga/src/annotation/annotate_rr_graph.cpp b/openfpga/src/annotation/annotate_rr_graph.cpp index 351c248bb..d0097ced6 100644 --- a/openfpga/src/annotation/annotate_rr_graph.cpp +++ b/openfpga/src/annotation/annotate_rr_graph.cpp @@ -524,13 +524,16 @@ void annotate_direct_circuit_models(const DeviceContext& vpr_device_ctx, for (int idirect = 0; idirect < vpr_device_ctx.arch->num_directs; ++idirect) { std::string direct_name = vpr_device_ctx.arch->Directs[idirect].name; - CircuitModelId circuit_model = CircuitModelId::INVALID(); /* The name-to-circuit mapping is stored in either cb_switch-to-circuit or sb_switch-to-circuit, * Try to find one and update the device annotation */ - if (0 < openfpga_arch.direct2circuit.count(direct_name)) { - circuit_model = openfpga_arch.direct2circuit.at(direct_name); + ArchDirectId direct_id = openfpga_arch.arch_direct.direct(direct_name); + /* Cannot find a direct, no annotation needed for this direct */ + if (ArchDirectId::INVALID() == direct_id) { + continue; } + + CircuitModelId circuit_model = openfpga_arch.arch_direct.circuit_model(direct_id); /* Cannot find a circuit model, error out! */ if (CircuitModelId::INVALID() == circuit_model) { VTR_LOG_ERROR("Fail to find a circuit model for a direct connection '%s'!\nPlease check your OpenFPGA architecture XML!\n", @@ -547,7 +550,7 @@ void annotate_direct_circuit_models(const DeviceContext& vpr_device_ctx, } /* Now update the device annotation */ - vpr_device_annotation.add_direct_circuit_model(idirect, circuit_model); + vpr_device_annotation.add_direct_annotation(idirect, direct_id); VTR_LOGV(verbose_output, "Binded a direct connection '%s' to circuit model '%s'\n", direct_name.c_str(), diff --git a/openfpga/src/annotation/vpr_device_annotation.cpp b/openfpga/src/annotation/vpr_device_annotation.cpp index aa2e6fce9..5b36e3885 100644 --- a/openfpga/src/annotation/vpr_device_annotation.cpp +++ b/openfpga/src/annotation/vpr_device_annotation.cpp @@ -233,13 +233,12 @@ CircuitModelId VprDeviceAnnotation::rr_segment_circuit_model(const RRSegmentId& return rr_segment_circuit_models_.at(rr_segment); } -CircuitModelId VprDeviceAnnotation::direct_circuit_model(const size_t& direct) const { +ArchDirectId VprDeviceAnnotation::direct_annotation(const size_t& direct) const { /* Ensure that the rr_switch is in the list */ - std::map::const_iterator it = direct_circuit_models_.find(direct); - if (it == direct_circuit_models_.end()) { - return CircuitModelId::INVALID(); + if (0 == direct_annotations_.count(direct)) { + return ArchDirectId::INVALID(); } - return direct_circuit_models_.at(direct); + return direct_annotations_.at(direct); } /************************************************************************ @@ -461,15 +460,14 @@ void VprDeviceAnnotation::add_rr_segment_circuit_model(const RRSegmentId& rr_seg rr_segment_circuit_models_[rr_segment] = circuit_model; } -void VprDeviceAnnotation::add_direct_circuit_model(const size_t& direct, const CircuitModelId& circuit_model) { +void VprDeviceAnnotation::add_direct_annotation(const size_t& direct, const ArchDirectId& arch_direct_id) { /* Warn any override attempt */ - std::map::const_iterator it = direct_circuit_models_.find(direct); - if (it != direct_circuit_models_.end()) { - VTR_LOG_WARN("Override the annotation between direct '%ld' and its circuit_model '%ld'!\n", - size_t(direct), size_t(circuit_model)); + if (0 < direct_annotations_.count(direct)) { + VTR_LOG_WARN("Override the annotation between direct '%ld' and its annotation '%ld'!\n", + size_t(direct), size_t(arch_direct_id)); } - direct_circuit_models_[direct] = circuit_model; + direct_annotations_[direct] = arch_direct_id; } } /* End namespace openfpga*/ diff --git a/openfpga/src/annotation/vpr_device_annotation.h b/openfpga/src/annotation/vpr_device_annotation.h index 74868ae61..5c083e367 100644 --- a/openfpga/src/annotation/vpr_device_annotation.h +++ b/openfpga/src/annotation/vpr_device_annotation.h @@ -18,6 +18,7 @@ /* Header from openfpgautil library */ #include "openfpga_port.h" #include "circuit_library.h" +#include "arch_direct.h" /* Begin namespace openfpga */ namespace openfpga { @@ -71,7 +72,7 @@ class VprDeviceAnnotation { t_pb_graph_pin* physical_pb_graph_pin(t_pb_graph_pin* pb_graph_pin) const; CircuitModelId rr_switch_circuit_model(const RRSwitchId& rr_switch) const; CircuitModelId rr_segment_circuit_model(const RRSegmentId& rr_segment) const; - CircuitModelId direct_circuit_model(const size_t& direct) const; + ArchDirectId direct_annotation(const size_t& direct) const; public: /* Public mutators */ void add_pb_type_physical_mode(t_pb_type* pb_type, t_mode* physical_mode); void add_physical_pb_type(t_pb_type* operating_pb_type, t_pb_type* physical_pb_type); @@ -91,7 +92,7 @@ class VprDeviceAnnotation { void add_physical_pb_graph_pin(t_pb_graph_pin* operating_pb_graph_pin, t_pb_graph_pin* physical_pb_graph_pin); void add_rr_switch_circuit_model(const RRSwitchId& rr_switch, const CircuitModelId& circuit_model); void add_rr_segment_circuit_model(const RRSegmentId& rr_segment, const CircuitModelId& circuit_model); - void add_direct_circuit_model(const size_t& direct, const CircuitModelId& circuit_model); + void add_direct_annotation(const size_t& direct, const ArchDirectId& arch_direct_id); private: /* Internal data */ /* Pair a regular pb_type to its physical pb_type */ std::map physical_pb_types_; @@ -173,8 +174,8 @@ class VprDeviceAnnotation { /* Pair a Routing Segment (rr_segment) to a circuit model */ std::map rr_segment_circuit_models_; - /* Pair a direct connection (direct) to a circuit model */ - std::map direct_circuit_models_; + /* Pair a direct connection (direct) to a annotation which contains circuit model id */ + std::map direct_annotations_; }; } /* End namespace openfpga*/ diff --git a/openfpga/src/base/openfpga_context.h b/openfpga/src/base/openfpga_context.h index 6f9ee0910..ac3cc99c8 100644 --- a/openfpga/src/base/openfpga_context.h +++ b/openfpga/src/base/openfpga_context.h @@ -8,6 +8,7 @@ #include "vpr_clustering_annotation.h" #include "vpr_routing_annotation.h" #include "mux_library.h" +#include "tile_direct.h" #include "module_manager.h" #include "device_rr_gsb.h" @@ -47,6 +48,7 @@ class OpenfpgaContext : public Context { const openfpga::VprRoutingAnnotation& vpr_routing_annotation() const { return vpr_routing_annotation_; } const openfpga::DeviceRRGSB& device_rr_gsb() const { return device_rr_gsb_; } const openfpga::MuxLibrary& mux_lib() const { return mux_lib_; } + const openfpga::TileDirect& tile_direct() const { return tile_direct_; } const openfpga::ModuleManager& module_graph() const { return module_graph_; } public: /* Public mutators */ openfpga::Arch& mutable_arch() { return arch_; } @@ -56,6 +58,7 @@ class OpenfpgaContext : public Context { openfpga::VprRoutingAnnotation& mutable_vpr_routing_annotation() { return vpr_routing_annotation_; } openfpga::DeviceRRGSB& mutable_device_rr_gsb() { return device_rr_gsb_; } openfpga::MuxLibrary& mutable_mux_lib() { return mux_lib_; } + openfpga::TileDirect& mutable_tile_direct() { return tile_direct_; } openfpga::ModuleManager& mutable_module_graph() { return module_graph_; } private: /* Internal data */ /* Data structure to store information from read_openfpga_arch library */ @@ -67,7 +70,7 @@ class OpenfpgaContext : public Context { /* Naming fix to netlist */ openfpga::VprNetlistAnnotation vpr_netlist_annotation_; - /* TODO: Pin net fix to cluster results */ + /* Pin net fix to cluster results */ openfpga::VprClusteringAnnotation vpr_clustering_annotation_; /* Routing results annotation */ @@ -79,6 +82,9 @@ class OpenfpgaContext : public Context { /* Library of physical implmentation of routing multiplexers */ openfpga::MuxLibrary mux_lib_; + /* Inner/inter-column/row tile direct connections */ + openfpga::TileDirect tile_direct_; + /* Fabric module graph */ openfpga::ModuleManager module_graph_; }; diff --git a/openfpga/src/base/openfpga_link_arch.cpp b/openfpga/src/base/openfpga_link_arch.cpp index 78afc93a2..7ee522b6a 100644 --- a/openfpga/src/base/openfpga_link_arch.cpp +++ b/openfpga/src/base/openfpga_link_arch.cpp @@ -14,6 +14,7 @@ #include "annotate_routing.h" #include "annotate_rr_graph.h" #include "mux_library_builder.h" +#include "build_tile_direct.h" #include "openfpga_link_arch.h" /* Include global variables of VPR */ @@ -106,6 +107,12 @@ void link_arch(OpenfpgaContext& openfpga_context, /* Build multiplexer library */ openfpga_context.mutable_mux_lib() = build_device_mux_library(g_vpr_ctx.device(), const_cast(openfpga_context)); + + /* Build tile direct annotation */ + openfpga_context.mutable_tile_direct() = build_device_tile_direct(g_vpr_ctx.device(), + openfpga_context.arch().arch_direct, + openfpga_context.arch().circuit_lib); + } } /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_connection.cpp b/openfpga/src/fabric/build_top_module_connection.cpp new file mode 100644 index 000000000..f1895305e --- /dev/null +++ b/openfpga/src/fabric/build_top_module_connection.cpp @@ -0,0 +1,676 @@ +/******************************************************************** + * This file include most utilized functions for building connections + * inside the module graph for FPGA fabric + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" + +/* Headers from openfpgautil library */ +#include "openfpga_side_manager.h" + +#include "openfpga_reserved_words.h" +#include "openfpga_naming.h" +#include "pb_type_utils.h" +#include "rr_gsb_utils.h" + +#include "build_top_module_utils.h" +#include "build_top_module_connection.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Add module nets to connect a GSB to adjacent grid ports/pins + * as well as connection blocks + * This function will create nets for the following types of connections + * between grid output pins of Switch block and adjacent grids + * In this case, the net source is the grid pin, while the net sink + * is the switch block pin + * + * +------------+ +------------+ + * | | | | + * | Grid | | Grid | + * | [x][y+1] | | [x+1][y+1] | + * | |----+ +----| | + * +------------+ | | +------------+ + * | v v | + * | +------------+ | + * +------>| |<-----+ + * | Switch | + * | Block | + * +------>| [x][y] |<-----+ + * | +------------+ | + * | ^ ^ | + * | | | | + * +------------+ | | +------------+ + * | |----+ +-----| | + * | Grid | | Grid | + * | [x][y] | | [x+1][y] | + * | | | | + * +------------+ +------------+ + + * + *******************************************************************/ +static +void add_top_module_nets_connect_grids_and_sb(ModuleManager& module_manager, + const ModuleId& top_module, + const DeviceGrid& grids, + const vtr::Matrix& grid_instance_ids, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb, + const RRGSB& rr_gsb, + const vtr::Matrix& sb_instance_ids, + const bool& compact_routing_hierarchy) { + + /* We could have two different coordinators, one is the instance, the other is the module */ + vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + vtr::Point module_gsb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + + /* If we use compact routing hierarchy, we should find the unique module of CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + vtr::Point gsb_coord(rr_gsb.get_x(), rr_gsb.get_y()); + const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(gsb_coord); + module_gsb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_coordinate.set_y(unique_mirror.get_y()); + } + + /* This is the source cb that is added to the top module */ + const RRGSB& module_sb = device_rr_gsb.get_gsb(module_gsb_coordinate); + vtr::Point module_sb_coordinate(module_sb.get_sb_x(), module_sb.get_sb_y()); + + /* Collect sink-related information */ + std::string sink_sb_module_name = generate_switch_block_module_name(module_sb_coordinate); + ModuleId sink_sb_module = module_manager.find_module(sink_sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sink_sb_module)); + size_t sink_sb_instance = sb_instance_ids[instance_sb_coordinate.x()][instance_sb_coordinate.y()]; + + /* Connect grid output pins (OPIN) to switch block grid pins */ + for (size_t side = 0; side < module_sb.get_num_sides(); ++side) { + SideManager side_manager(side); + for (size_t inode = 0; inode < module_sb.get_num_opin_nodes(side_manager.get_side()); ++inode) { + /* Collect source-related information */ + /* Generate the grid module name by considering if it locates on the border */ + vtr::Point grid_coordinate(rr_graph.node_xlow(rr_gsb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_ylow(rr_gsb.get_opin_node(side_manager.get_side(), inode))); + std::string src_grid_module_name = generate_grid_block_module_name_in_top_module(std::string(GRID_MODULE_NAME_PREFIX), grids, grid_coordinate); + ModuleId src_grid_module = module_manager.find_module(src_grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(src_grid_module)); + size_t src_grid_instance = grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()]; + size_t src_grid_pin_index = rr_graph.node_pin_num(rr_gsb.get_opin_node(side_manager.get_side(), inode)); + size_t src_grid_pin_width = grids[grid_coordinate.x()][grid_coordinate.y()].type->pin_width_offset[src_grid_pin_index]; + size_t src_grid_pin_height = grids[grid_coordinate.x()][grid_coordinate.y()].type->pin_height_offset[src_grid_pin_index]; + std::string src_grid_port_name = generate_grid_port_name(grid_coordinate, src_grid_pin_width, src_grid_pin_height, + rr_graph.node_side(rr_gsb.get_opin_node(side_manager.get_side(), inode)), + src_grid_pin_index, false); + ModulePortId src_grid_port_id = module_manager.find_module_port(src_grid_module, src_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(src_grid_module, src_grid_port_id)); + BasicPort src_grid_port = module_manager.module_port(src_grid_module, src_grid_port_id); + + /* Collect sink-related information */ + vtr::Point sink_sb_port_coord(rr_graph.node_xlow(module_sb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_ylow(module_sb.get_opin_node(side_manager.get_side(), inode))); + std::string sink_sb_port_name = generate_sb_module_grid_port_name(side_manager.get_side(), + rr_graph.node_side(module_sb.get_opin_node(side_manager.get_side(), inode)), + src_grid_pin_index); + ModulePortId sink_sb_port_id = module_manager.find_module_port(sink_sb_module, sink_sb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(sink_sb_module, sink_sb_port_id)); + BasicPort sink_sb_port = module_manager.module_port(sink_sb_module, sink_sb_port_id); + + /* Source and sink port should match in size */ + VTR_ASSERT(src_grid_port.get_width() == sink_sb_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(top_module); + /* Configure the net source */ + module_manager.add_module_net_source(top_module, net, src_grid_module, src_grid_instance, src_grid_port_id, src_grid_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(top_module, net, sink_sb_module, sink_sb_instance, sink_sb_port_id, sink_sb_port.pins()[pin_id]); + } + } + } +} + +/******************************************************************** + * Add module nets to connect a GSB to adjacent grid ports/pins + * as well as connection blocks + * This function will create nets for the following types of connections + * between grid output pins of Switch block and adjacent grids + * In this case, the net source is the grid pin, while the net sink + * is the switch block pin + * + * In particular, this function considers the duplicated output pins of grids + * when creating the connecting nets. + * The follow figure shows the different pin postfix to be considered when + * connecting the grid pins to SB inputs + * + * +------------+ +------------+ + * | | | | + * | Grid | | Grid | + * | [x][y+1] |lower lower| [x+1][y+1] | + * | |----+ +----| | + * +------------+ | | +------------+ + * |lower v v |upper + * | +------------+ | + * +------>| |<-----+ + * | Switch | + * | Block | + * +------>| [x][y] |<-----+ + * | +------------+ | + * | ^ ^ | + * |lower | | |upper + * +------------+ | | +------------+ + * | |----+ +-----| | + * | Grid |upper upper | Grid | + * | [x][y] | | [x+1][y] | + * | | | | + * +------------+ +------------+ + * + *******************************************************************/ +static +void add_top_module_nets_connect_grids_and_sb_with_duplicated_pins(ModuleManager& module_manager, + const ModuleId& top_module, + const DeviceGrid& grids, + const vtr::Matrix& grid_instance_ids, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb, + const RRGSB& rr_gsb, + const vtr::Matrix& sb_instance_ids, + const bool& compact_routing_hierarchy) { + + /* We could have two different coordinators, one is the instance, the other is the module */ + vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + vtr::Point module_gsb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + + /* If we use compact routing hierarchy, we should find the unique module of CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + vtr::Point gsb_coord(rr_gsb.get_x(), rr_gsb.get_y()); + const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(gsb_coord); + module_gsb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_coordinate.set_y(unique_mirror.get_y()); + } + + /* This is the source cb that is added to the top module */ + const RRGSB& module_sb = device_rr_gsb.get_gsb(module_gsb_coordinate); + vtr::Point module_sb_coordinate(module_sb.get_sb_x(), module_sb.get_sb_y()); + + /* Collect sink-related information */ + std::string sink_sb_module_name = generate_switch_block_module_name(module_sb_coordinate); + ModuleId sink_sb_module = module_manager.find_module(sink_sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sink_sb_module)); + size_t sink_sb_instance = sb_instance_ids[instance_sb_coordinate.x()][instance_sb_coordinate.y()]; + + /* Create a truth table for the postfix to be used regarding to the different side of switch blocks */ + std::map sb_side2postfix_map; + /* Boolean variable "true" indicates the upper postfix in naming functions + * Boolean variable "false" indicates the lower postfix in naming functions + */ + sb_side2postfix_map[TOP] = false; + sb_side2postfix_map[RIGHT] = true; + sb_side2postfix_map[BOTTOM] = true; + sb_side2postfix_map[LEFT] = false; + + /* Connect grid output pins (OPIN) to switch block grid pins */ + for (size_t side = 0; side < module_sb.get_num_sides(); ++side) { + SideManager side_manager(side); + for (size_t inode = 0; inode < module_sb.get_num_opin_nodes(side_manager.get_side()); ++inode) { + /* Collect source-related information */ + /* Generate the grid module name by considering if it locates on the border */ + vtr::Point grid_coordinate(rr_graph.node_xlow(rr_gsb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_ylow(rr_gsb.get_opin_node(side_manager.get_side(), inode))); + std::string src_grid_module_name = generate_grid_block_module_name_in_top_module(std::string(GRID_MODULE_NAME_PREFIX), grids, grid_coordinate); + ModuleId src_grid_module = module_manager.find_module(src_grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(src_grid_module)); + size_t src_grid_instance = grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()]; + size_t src_grid_pin_index = rr_graph.node_pin_num(rr_gsb.get_opin_node(side_manager.get_side(), inode)); + size_t src_grid_pin_width = grids[grid_coordinate.x()][grid_coordinate.y()].type->pin_width_offset[src_grid_pin_index]; + size_t src_grid_pin_height = grids[grid_coordinate.x()][grid_coordinate.y()].type->pin_height_offset[src_grid_pin_index]; + + /* Pins for direct connection are NOT duplicated. + * Follow the traditional recipe when adding nets! + * Xifan: I assume that each direct connection pin must have Fc=0. + * For other duplicated pins, we follow the new naming + */ + std::string src_grid_port_name; + if (0. == grids[grid_coordinate.x()][grid_coordinate.y()].type->fc_specs[src_grid_pin_index].fc_value) { + src_grid_port_name = generate_grid_port_name(grid_coordinate, src_grid_pin_width, src_grid_pin_height, + rr_graph.node_side(rr_gsb.get_opin_node(side_manager.get_side(), inode)), + src_grid_pin_index, false); + } else { + src_grid_port_name = generate_grid_duplicated_port_name(src_grid_pin_width, src_grid_pin_height, + rr_graph.node_side(rr_gsb.get_opin_node(side_manager.get_side(), inode)), + src_grid_pin_index, sb_side2postfix_map[side_manager.get_side()]); + } + ModulePortId src_grid_port_id = module_manager.find_module_port(src_grid_module, src_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(src_grid_module, src_grid_port_id)); + BasicPort src_grid_port = module_manager.module_port(src_grid_module, src_grid_port_id); + + /* Collect sink-related information */ + vtr::Point sink_sb_port_coord(rr_graph.node_xlow(module_sb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_ylow(module_sb.get_opin_node(side_manager.get_side(), inode))); + std::string sink_sb_port_name = generate_sb_module_grid_port_name(side_manager.get_side(), + rr_graph.node_side(module_sb.get_opin_node(side_manager.get_side(), inode)), + src_grid_pin_index); + ModulePortId sink_sb_port_id = module_manager.find_module_port(sink_sb_module, sink_sb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(sink_sb_module, sink_sb_port_id)); + BasicPort sink_sb_port = module_manager.module_port(sink_sb_module, sink_sb_port_id); + + /* Source and sink port should match in size */ + VTR_ASSERT(src_grid_port.get_width() == sink_sb_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_grid_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(top_module); + /* Configure the net source */ + module_manager.add_module_net_source(top_module, net, src_grid_module, src_grid_instance, src_grid_port_id, src_grid_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(top_module, net, sink_sb_module, sink_sb_instance, sink_sb_port_id, sink_sb_port.pins()[pin_id]); + } + } + } +} + +/******************************************************************** + * This function will create nets for the connections + * between grid input pins and connection blocks + * In this case, the net source is the connection block pin, + * while the net sink is the grid input + * + * +------------+ +------------------+ +------------+ + * | | | | | | + * | Grid |<-----| Connection Block |----->| Grid | + * | [x][y+1] | | Y-direction | | [x+1][y+1] | + * | | | [x][y+1] | | | + * +------------+ +------------------+ +------------+ + * ^ + * | + * +------------+ +------------------+ + * | Connection | | | + * | Block | | Switch Block | + * | X-direction| | [x][y] | + * | [x][y] | | | + * +------------+ +------------------+ + * | + * v + * +------------+ + * | | + * | Grid | + * | [x][y] | + * | | + * +------------+ + * + * + * Relationship between source connection block and its unique module + * Take an example of a CBY + * + * grid_pin name should follow unique module of Grid[x][y+1] + * cb_pin name should follow unique module of CBY[x][y+1] + * + * However, instace id should follow the origin Grid and Connection block + * + * + * +------------+ +------------------+ + * | | | | + * | Grid |<------------| Connection Block | + * | [x][y+1] | | Y-direction | + * | | | [x][y+1] | + * +------------+ +------------------+ + * ^ + * || unique mirror + * +------------+ +------------------+ + * | | | | + * | Grid |<------------| Connection Block | + * | [i][j+1] | | Y-direction | + * | | | [i][j+1] | + * +------------+ +------------------+ + * + *******************************************************************/ +static +void add_top_module_nets_connect_grids_and_cb(ModuleManager& module_manager, + const ModuleId& top_module, + const DeviceGrid& grids, + const vtr::Matrix& grid_instance_ids, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const vtr::Matrix& cb_instance_ids, + const bool& compact_routing_hierarchy) { + /* We could have two different coordinators, one is the instance, the other is the module */ + vtr::Point instance_cb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + vtr::Point module_gsb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + + /* Skip those Connection blocks that do not exist */ + if (false == rr_gsb.is_cb_exist(cb_type)) { + return; + } + + /* Skip if the cb does not contain any configuration bits! */ + if (true == connection_block_contain_only_routing_tracks(rr_gsb, cb_type)) { + return; + } + + /* If we use compact routing hierarchy, we should find the unique module of CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + vtr::Point gsb_coord(rr_gsb.get_x(), rr_gsb.get_y()); + const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(cb_type, gsb_coord); + module_gsb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_coordinate.set_y(unique_mirror.get_y()); + } + + /* This is the source cb that is added to the top module */ + const RRGSB& module_cb = device_rr_gsb.get_gsb(module_gsb_coordinate); + vtr::Point module_cb_coordinate(module_cb.get_cb_x(cb_type), module_cb.get_cb_y(cb_type)); + + /* Collect source-related information */ + std::string src_cb_module_name = generate_connection_block_module_name(cb_type, module_cb_coordinate); + ModuleId src_cb_module = module_manager.find_module(src_cb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(src_cb_module)); + /* Instance id should follow the instance cb coordinate */ + size_t src_cb_instance = cb_instance_ids[instance_cb_coordinate.x()][instance_cb_coordinate.y()]; + + /* Iterate over the output pins of the Connection Block */ + std::vector cb_ipin_sides = module_cb.get_cb_ipin_sides(cb_type); + for (size_t iside = 0; iside < cb_ipin_sides.size(); ++iside) { + enum e_side cb_ipin_side = cb_ipin_sides[iside]; + for (size_t inode = 0; inode < module_cb.get_num_ipin_nodes(cb_ipin_side); ++inode) { + /* Collect source-related information */ + RRNodeId module_ipin_node = module_cb.get_ipin_node(cb_ipin_side, inode); + vtr::Point cb_src_port_coord(rr_graph.node_xlow(module_ipin_node), + rr_graph.node_ylow(module_ipin_node)); + std::string src_cb_port_name = generate_cb_module_grid_port_name(cb_ipin_side, + rr_graph.node_pin_num(module_ipin_node)); + ModulePortId src_cb_port_id = module_manager.find_module_port(src_cb_module, src_cb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(src_cb_module, src_cb_port_id)); + BasicPort src_cb_port = module_manager.module_port(src_cb_module, src_cb_port_id); + + /* Collect sink-related information */ + /* Note that we use the instance cb pin here!!! + * because it has the correct coordinator for the grid!!! + */ + RRNodeId instance_ipin_node = rr_gsb.get_ipin_node(cb_ipin_side, inode); + vtr::Point grid_coordinate(rr_graph.node_xlow(instance_ipin_node), + rr_graph.node_ylow(instance_ipin_node)); + std::string sink_grid_module_name = generate_grid_block_module_name_in_top_module(std::string(GRID_MODULE_NAME_PREFIX), grids, grid_coordinate); + ModuleId sink_grid_module = module_manager.find_module(sink_grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sink_grid_module)); + size_t sink_grid_instance = grid_instance_ids[grid_coordinate.x()][grid_coordinate.y()]; + size_t sink_grid_pin_index = rr_graph.node_pin_num(instance_ipin_node); + size_t sink_grid_pin_width = grids[grid_coordinate.x()][grid_coordinate.y()].type->pin_width_offset[sink_grid_pin_index]; + size_t sink_grid_pin_height = grids[grid_coordinate.x()][grid_coordinate.y()].type->pin_height_offset[sink_grid_pin_index]; + std::string sink_grid_port_name = generate_grid_port_name(grid_coordinate, sink_grid_pin_width, sink_grid_pin_height, + rr_graph.node_side(rr_gsb.get_ipin_node(cb_ipin_side, inode)), + sink_grid_pin_index, false); + ModulePortId sink_grid_port_id = module_manager.find_module_port(sink_grid_module, sink_grid_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(sink_grid_module, sink_grid_port_id)); + BasicPort sink_grid_port = module_manager.module_port(sink_grid_module, sink_grid_port_id); + + /* Source and sink port should match in size */ + VTR_ASSERT(src_cb_port.get_width() == sink_grid_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < src_cb_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(top_module); + /* Configure the net source */ + module_manager.add_module_net_source(top_module, net, src_cb_module, src_cb_instance, src_cb_port_id, src_cb_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(top_module, net, sink_grid_module, sink_grid_instance, sink_grid_port_id, sink_grid_port.pins()[pin_id]); + } + } + } +} + +/******************************************************************** + * This function will create nets for the connections + * between connection block and switch block pins + * Two cases should be considered: + * a. The switch block pin denotes an input of a routing track + * The net source is an output of a routing track of connection block + * while the net sink is an input of a routing track of switch block + * b. The switch block pin denotes an output of a routing track + * The net source is an output of routing track of switch block + * while the net sink is an input of a routing track of connection block + * + * +------------+ +------------------+ +------------+ + * | | | | | | + * | Grid | | Connection Block | | Grid | + * | [x][y+1] | | Y-direction | | [x+1][y+1] | + * | | | [x][y+1] | | | + * +------------+ +------------------+ +------------+ + * | ^ + * v | + * +------------+ +------------------+ +------------+ + * | Connection |----->| |----->| Connection | + * | Block | | Switch Block | | Block | + * | X-direction|<-----| [x][y] |<-----| X-direction| + * | [x][y] | | | | [x+1][y] | + * +------------+ +------------------+ +------------+ + * | ^ + * v | + * +------------+ +------------------+ +------------+ + * | | | | | | + * | Grid | | Connection Block | | Grid | + * | [x][y] | | Y-direction | | [x][y+1] | + * | | | [x][y] | | | + * +------------+ +------------------+ +------------+ + * + * Here, to achieve the purpose, we can simply iterate over the + * four sides of switch block and make connections to adjancent + * connection blocks + * + *******************************************************************/ +static +void add_top_module_nets_connect_sb_and_cb(ModuleManager& module_manager, + const ModuleId& top_module, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb, + const RRGSB& rr_gsb, + const vtr::Matrix& sb_instance_ids, + const std::map>& cb_instance_ids, + const bool& compact_routing_hierarchy) { + /* We could have two different coordinators, one is the instance, the other is the module */ + vtr::Point instance_sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + vtr::Point module_gsb_sb_coordinate(rr_gsb.get_x(), rr_gsb.get_y()); + + /* Skip those Switch blocks that do not exist */ + if (false == rr_gsb.is_sb_exist()) { + return; + } + + /* If we use compact routing hierarchy, we should find the unique module of CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + vtr::Point gsb_coord(rr_gsb.get_x(), rr_gsb.get_y()); + const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(gsb_coord); + module_gsb_sb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_sb_coordinate.set_y(unique_mirror.get_y()); + } + + /* This is the source cb that is added to the top module */ + const RRGSB& module_sb = device_rr_gsb.get_gsb(module_gsb_sb_coordinate); + vtr::Point module_sb_coordinate(module_sb.get_sb_x(), module_sb.get_sb_y()); + std::string sb_module_name = generate_switch_block_module_name(module_sb_coordinate); + ModuleId sb_module_id = module_manager.find_module(sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sb_module_id)); + size_t sb_instance = sb_instance_ids[instance_sb_coordinate.x()][instance_sb_coordinate.y()]; + + /* Connect grid output pins (OPIN) to switch block grid pins */ + for (size_t side = 0; side < module_sb.get_num_sides(); ++side) { + SideManager side_manager(side); + /* Iterate over the routing tracks on this side */ + /* Early skip: if there is no routing tracks at this side */ + if (0 == module_sb.get_chan_width(side_manager.get_side())) { + continue; + } + /* Find the Connection Block module */ + /* We find the original connection block and then spot its unique mirror! + * Do NOT use module_sb here!!! + */ + t_rr_type cb_type = find_top_module_cb_type_by_sb_side(side_manager.get_side()); + vtr::Point instance_gsb_cb_coordinate = find_top_module_gsb_coordinate_by_sb_side(rr_gsb, side_manager.get_side()); + vtr::Point module_gsb_cb_coordinate = find_top_module_gsb_coordinate_by_sb_side(rr_gsb, side_manager.get_side()); + + /* Skip those Connection blocks that do not exist: + * 1. The CB does not exist in the device level! We should skip! + * 2. The CB does exist but we need to make sure if the GSB includes such CBs + * For TOP and LEFT side, check the existence using RRGSB method is_cb_exist() + * FOr RIGHT and BOTTOM side, find the adjacent RRGSB and then use is_cb_exist() + */ + if ( TOP == side_manager.get_side() || LEFT == side_manager.get_side() ) { + if ( false == rr_gsb.is_cb_exist(cb_type)) { + continue; + } + } + + if ( RIGHT == side_manager.get_side() || BOTTOM == side_manager.get_side() ) { + const RRGSB& adjacent_gsb = device_rr_gsb.get_gsb(module_gsb_cb_coordinate); + if ( false == adjacent_gsb.is_cb_exist(cb_type)) { + continue; + } + } + + /* If we use compact routing hierarchy, we should find the unique module of CB, which is added to the top module */ + if (true == compact_routing_hierarchy) { + const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(cb_type, module_gsb_cb_coordinate); + module_gsb_cb_coordinate.set_x(unique_mirror.get_x()); + module_gsb_cb_coordinate.set_y(unique_mirror.get_y()); + } + + const RRGSB& module_cb = device_rr_gsb.get_gsb(module_gsb_cb_coordinate); + vtr::Point module_cb_coordinate(module_cb.get_cb_x(cb_type), module_cb.get_cb_y(cb_type)); + std::string cb_module_name = generate_connection_block_module_name(cb_type, module_cb_coordinate); + ModuleId cb_module_id = module_manager.find_module(cb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(cb_module_id)); + const RRGSB& instance_cb = device_rr_gsb.get_gsb(instance_gsb_cb_coordinate); + vtr::Point instance_cb_coordinate(instance_cb.get_cb_x(cb_type), instance_cb.get_cb_y(cb_type)); + size_t cb_instance = cb_instance_ids.at(cb_type)[instance_cb_coordinate.x()][instance_cb_coordinate.y()]; + + for (size_t itrack = 0; itrack < module_sb.get_chan_width(side_manager.get_side()); ++itrack) { + std::string sb_port_name = generate_sb_module_track_port_name(rr_graph.node_type(module_sb.get_chan_node(side_manager.get_side(), itrack)), + side_manager.get_side(), itrack, + module_sb.get_chan_node_direction(side_manager.get_side(), itrack)); + /* Prepare SB-related port information */ + ModulePortId sb_port_id = module_manager.find_module_port(sb_module_id, sb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module_id, sb_port_id)); + BasicPort sb_port = module_manager.module_port(sb_module_id, sb_port_id); + + /* Prepare CB-related port information */ + PORTS cb_port_direction = OUT_PORT; + /* The cb port direction should be opposite to the sb port !!! */ + if (OUT_PORT == module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { + cb_port_direction = IN_PORT; + } else { + VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction(side_manager.get_side(), itrack)); + } + std::string cb_port_name = generate_cb_module_track_port_name(cb_type, + itrack, + cb_port_direction); + ModulePortId cb_port_id = module_manager.find_module_port(cb_module_id, cb_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module_id, cb_port_id)); + BasicPort cb_port = module_manager.module_port(cb_module_id, cb_port_id); + + /* Source and sink port should match in size */ + VTR_ASSERT(cb_port.get_width() == sb_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < cb_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(top_module); + /* Configure the net source and sink: + * If sb port is an output (source), cb port is an input (sink) + * If sb port is an input (sink), cb port is an output (source) + */ + if (OUT_PORT == module_sb.get_chan_node_direction(side_manager.get_side(), itrack)) { + module_manager.add_module_net_sink(top_module, net, cb_module_id, cb_instance, cb_port_id, cb_port.pins()[pin_id]); + module_manager.add_module_net_source(top_module, net, sb_module_id, sb_instance, sb_port_id, sb_port.pins()[pin_id]); + } else { + VTR_ASSERT(IN_PORT == module_sb.get_chan_node_direction(side_manager.get_side(), itrack)); + module_manager.add_module_net_source(top_module, net, cb_module_id, cb_instance, cb_port_id, cb_port.pins()[pin_id]); + module_manager.add_module_net_sink(top_module, net, sb_module_id, sb_instance, sb_port_id, sb_port.pins()[pin_id]); + } + } + } + } +} + +/******************************************************************** + * Add module nets to connect the grid ports/pins to Connection Blocks + * and Switch Blocks + * To make it easy, this function will iterate over all the General + * Switch Blocks (GSBs), through which we can obtain the coordinates + * of all the grids, connection blocks and switch blocks that are + * supposed to be connected tightly. + * + * As such, we have completed all the connection for each grid. + * There is no need to iterate over the grids + * + * +-------------------------+ +---------------------------------+ + * | | | Y-direction CB | + * | Grid[x][y+1] | | [x][y + 1] | + * | | +---------------------------------+ + * +-------------------------+ + * TOP SIDE + * +-------------+ +---------------------------------+ + * | | | OPIN_NODE CHAN_NODES OPIN_NODES | + * | | | | + * | | | OPIN_NODES OPIN_NODES | + * | X-direction | | | + * | CB | LEFT SIDE | Switch Block | RIGHT SIDE + * | [x][y] | | [x][y] | + * | | | | + * | | | CHAN_NODES CHAN_NODES | + * | | | | + * | | | OPIN_NODES OPIN_NODES | + * | | | | + * | | | OPIN_NODE CHAN_NODES OPIN_NODES | + * +-------------+ +---------------------------------+ + * BOTTOM SIDE + *******************************************************************/ +void add_top_module_nets_connect_grids_and_gsbs(ModuleManager& module_manager, + const ModuleId& top_module, + const DeviceGrid& grids, + const vtr::Matrix& grid_instance_ids, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb, + const vtr::Matrix& sb_instance_ids, + const std::map>& cb_instance_ids, + const bool& compact_routing_hierarchy, + const bool& duplicate_grid_pin) { + + vtr::Point gsb_range = device_rr_gsb.get_gsb_range(); + + for (size_t ix = 0; ix < gsb_range.x(); ++ix) { + for (size_t iy = 0; iy < gsb_range.y(); ++iy) { + vtr::Point gsb_coordinate(ix, iy); + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); + /* Connect the grid pins of the GSB to adjacent grids */ + if (false == duplicate_grid_pin) { + add_top_module_nets_connect_grids_and_sb(module_manager, top_module, + grids, grid_instance_ids, + rr_graph, device_rr_gsb, rr_gsb, sb_instance_ids, + compact_routing_hierarchy); + } else { + VTR_ASSERT_SAFE(true == duplicate_grid_pin); + add_top_module_nets_connect_grids_and_sb_with_duplicated_pins(module_manager, top_module, + grids, grid_instance_ids, + rr_graph, device_rr_gsb, rr_gsb, sb_instance_ids, + compact_routing_hierarchy); + } + + add_top_module_nets_connect_grids_and_cb(module_manager, top_module, + grids, grid_instance_ids, + rr_graph, device_rr_gsb, rr_gsb, CHANX, cb_instance_ids.at(CHANX), + compact_routing_hierarchy); + + add_top_module_nets_connect_grids_and_cb(module_manager, top_module, + grids, grid_instance_ids, + rr_graph, device_rr_gsb, rr_gsb, CHANY, cb_instance_ids.at(CHANY), + compact_routing_hierarchy); + + add_top_module_nets_connect_sb_and_cb(module_manager, top_module, + rr_graph, device_rr_gsb, rr_gsb, sb_instance_ids, cb_instance_ids, + compact_routing_hierarchy); + + } + } +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_connection.h b/openfpga/src/fabric/build_top_module_connection.h new file mode 100644 index 000000000..7f7d06905 --- /dev/null +++ b/openfpga/src/fabric/build_top_module_connection.h @@ -0,0 +1,35 @@ +#ifndef BUILD_TOP_MODULE_CONNECTION_H +#define BUILD_TOP_MODULE_CONNECTION_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "vtr_geometry.h" +#include "vtr_ndmatrix.h" +#include "device_grid.h" +#include "rr_graph_obj.h" +#include "device_rr_gsb.h" +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void add_top_module_nets_connect_grids_and_gsbs(ModuleManager& module_manager, + const ModuleId& top_module, + const DeviceGrid& grids, + const vtr::Matrix& grid_instance_ids, + const RRGraph& rr_graph, + const DeviceRRGSB& device_rr_gsb, + const vtr::Matrix& sb_instance_ids, + const std::map>& cb_instance_ids, + const bool& compact_routing_hierarchy, + const bool& duplicate_grid_pin); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp new file mode 100644 index 000000000..37f67f0be --- /dev/null +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -0,0 +1,447 @@ +/******************************************************************** + * This file includes functions that are used to organize memories + * in the top module of FPGA fabric + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from vpr library */ +#include "vpr_utils.h" + +#include "rr_gsb_utils.h" +#include "openfpga_reserved_words.h" +#include "openfpga_naming.h" + +#include "module_manager_utils.h" +#include "build_top_module_memory.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * This function adds the CBX/CBY of a tile + * to the memory modules and memory instances + * This function is designed for organizing memory modules in top-level + * module + *******************************************************************/ +static +void organize_top_module_tile_cb_modules(ModuleManager& module_manager, + const ModuleId& top_module, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, + const vtr::Matrix& cb_instance_ids, + const DeviceRRGSB& device_rr_gsb, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const bool& compact_routing_hierarchy) { + /* If the CB does not exist, we can skip addition */ + if ( false == rr_gsb.is_cb_exist(cb_type)) { + return; + } + + /* Skip if the cb does not contain any configuration bits! */ + if (true == connection_block_contain_only_routing_tracks(rr_gsb, cb_type)) { + return; + } + + vtr::Point cb_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + /* If we use compact routing hierarchy, we should instanciate the unique module of SB */ + if (true == compact_routing_hierarchy) { + /* Note: use GSB coordinate when inquire for unique modules!!! */ + const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(cb_type, vtr::Point(rr_gsb.get_x(), rr_gsb.get_y())); + cb_coord.set_x(unique_mirror.get_cb_x(cb_type)); + cb_coord.set_y(unique_mirror.get_cb_y(cb_type)); + } + + std::string cb_module_name = generate_connection_block_module_name(cb_type, cb_coord); + ModuleId cb_module = module_manager.find_module(cb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(cb_module)); + + /* Identify if this sub module includes configuration bits, + * we will update the memory module and instance list + */ + if (0 < find_module_num_config_bits(module_manager, cb_module, + circuit_lib, sram_model, + sram_orgz_type)) { + /* Note that use the original CB coodinate for instance id searching ! */ + module_manager.add_configurable_child(top_module, cb_module, cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)]); + } +} + +/******************************************************************** + * This function adds the SB, CBX, CBY and Grid of a tile + * to the memory modules and memory instances + * This function is designed for organizing memory modules in top-level + * module + *******************************************************************/ +static +void organize_top_module_tile_memory_modules(ModuleManager& module_manager, + const ModuleId& top_module, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, + const DeviceGrid& grids, + const vtr::Matrix& grid_instance_ids, + const DeviceRRGSB& device_rr_gsb, + const vtr::Matrix& sb_instance_ids, + const std::map>& cb_instance_ids, + const bool& compact_routing_hierarchy, + const vtr::Point& tile_coord, + const e_side& tile_border_side) { + + vtr::Point gsb_coord_range = device_rr_gsb.get_gsb_range(); + + vtr::Point gsb_coord(tile_coord.x(), tile_coord.y() - 1); + + /* We do NOT consider SB and CBs if the gsb is not in the range! */ + if ( (gsb_coord.x() < gsb_coord_range.x()) + && (gsb_coord.y() < gsb_coord_range.y()) ) { + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(gsb_coord.x(), gsb_coord.y()); + /* Find Switch Block: unique module id and instance id! + * Note that switch block does always exist in a GSB + */ + vtr::Point sb_coord(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + /* If we use compact routing hierarchy, we should instanciate the unique module of SB */ + if (true == compact_routing_hierarchy) { + const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(sb_coord); + sb_coord.set_x(unique_mirror.get_sb_x()); + sb_coord.set_y(unique_mirror.get_sb_y()); + } + std::string sb_module_name = generate_switch_block_module_name(sb_coord); + ModuleId sb_module = module_manager.find_module(sb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(sb_module)); + + /* Identify if this sub module includes configuration bits, + * we will update the memory module and instance list + */ + /* If the CB does not exist, we can skip addition */ + if ( true == rr_gsb.is_sb_exist()) { + if (0 < find_module_num_config_bits(module_manager, sb_module, + circuit_lib, sram_model, + sram_orgz_type)) { + module_manager.add_configurable_child(top_module, sb_module, sb_instance_ids[rr_gsb.get_sb_x()][rr_gsb.get_sb_y()]); + } + } + + /* Try to find and add CBX and CBY */ + organize_top_module_tile_cb_modules(module_manager, top_module, circuit_lib, + sram_orgz_type, sram_model, + cb_instance_ids.at(CHANX), + device_rr_gsb, rr_gsb, CHANX, + compact_routing_hierarchy); + + organize_top_module_tile_cb_modules(module_manager, top_module, circuit_lib, + sram_orgz_type, sram_model, + cb_instance_ids.at(CHANY), + device_rr_gsb, rr_gsb, CHANY, + compact_routing_hierarchy); + } + + /* Find the module name for this type of grid */ + t_physical_tile_type_ptr grid_type = grids[tile_coord.x()][tile_coord.y()].type; + + /* Skip EMPTY Grid */ + if (true == is_empty_type(grid_type)) { + return; + } + /* Skip width > 1 or height > 1 Grid, which should already been processed when offset=0 */ + if ( (0 < grids[tile_coord.x()][tile_coord.y()].width_offset) + || (0 < grids[tile_coord.x()][tile_coord.y()].height_offset) ) { + return; + } + + std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX); + std::string grid_module_name = generate_grid_block_module_name(grid_module_name_prefix, std::string(grid_type->name), is_io_type(grid_type), tile_border_side); + ModuleId grid_module = module_manager.find_module(grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); + + /* Identify if this sub module includes configuration bits, + * we will update the memory module and instance list + */ + if (0 < find_module_num_config_bits(module_manager, grid_module, + circuit_lib, sram_model, + sram_orgz_type)) { + module_manager.add_configurable_child(top_module, grid_module, grid_instance_ids[tile_coord.x()][tile_coord.y()]); + } +} + +/******************************************************************** + * Organize the list of memory modules and instances + * This function will record all the sub modules of the top-level module + * (those have memory ports) to two lists: + * 1. memory_modules records the module ids + * 2. memory_instances records the instance ids + * To keep a clean memory connection between sub modules and top-level module, + * the sequence of memory_modules and memory_instances will follow + * a chain of tiles considering their physical location + * + * Inter tile connection: + * +--------------------------------------------------------+ + * | +------+------+-----+------+ | + * | | I/O | I/O | ... | I/O | | + * | | TOP | TOP | | TOP | | + * | +------+------+-----+------+ | + * | +---------------------------------->tail | + * | +------+ | +------+------+-----+------+ +------+ | + * | | | | | | | | | | | | + * | | I/O | | | Tile | Tile | ... | Tile | | I/O | | + * | | LEFT | | | [h+1]| [h+2]| | [n] | |RIGHT | | + * | +------+ | +------+------+-----+------+ +------+ | + * | +-------------------------------+ | + * | ... ... ... ... ... | ... | + * | +-------------------------------+ | + * | +------+ | +------+------+-----+------+ +------+ | + * | | | | | | | | | | | | + * | | I/O | | | Tile | Tile | ... | Tile | | I/O | | + * | | LEFT | | | [i+1]| [i+2]| | [j] | |RIGHT | | + * | +------+ | +------+------+-----+------+ +------+ | + * | +-------------------------------+ | + * | +------+ +------+------+-----+------+ | +------+ | + * | | | | | | | | | | | | + * | | I/O | | Tile | Tile | ... | Tile | | | I/O | | + * | | LEFT | | [0] | [1] | | [i] | | |RIGHT | | + * | +------+ +------+------+-----+------+ | +------+ | + * +-------------------------------------------+ | + * +------+------+-----+------+ | + * | I/O | I/O | ... | I/O | | + * |BOTTOM|BOTTOM| |BOTTOM| | + * +------+------+-----+------+ | + * head >-----------------------------------------------+ + * + * Inner tile connection + * + * Tile + * +---------------+----------+ + * <-+---------------+ + | + * | | | | + * | CLB | | CBY | + * | +-|-+ | + * | | | | + * +---------------+----------+ + * | +-+----+-----+---<--- + * | CBX | SB | + * | | | + * +---------------+----------+ + * + *******************************************************************/ +void organize_top_module_memory_modules(ModuleManager& module_manager, + const ModuleId& top_module, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, + const DeviceGrid& grids, + const vtr::Matrix& grid_instance_ids, + const DeviceRRGSB& device_rr_gsb, + const vtr::Matrix& sb_instance_ids, + const std::map>& cb_instance_ids, + const bool& compact_routing_hierarchy) { + + /* Ensure clean vectors to return */ + VTR_ASSERT(true == module_manager.configurable_children(top_module).empty()); + + /* First, organize the I/O tiles on the border */ + /* Special for the I/O tileas on RIGHT and BOTTOM, + * which are only I/O blocks, which do NOT contain CBs and SBs + */ + std::vector io_sides{BOTTOM, RIGHT, TOP, LEFT}; + std::map>> io_coords; + + /* BOTTOM side I/Os */ + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + io_coords[BOTTOM].push_back(vtr::Point(ix, 0)); + } + + /* RIGHT side I/Os */ + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + io_coords[RIGHT].push_back(vtr::Point(grids.width() - 1, iy)); + } + + /* TOP side I/Os + * Special case for TOP side: We need tile at ix = 0, which has a SB!!! + * + * TOP-LEFT CORNER of FPGA fabric + * + * +--------+ +-------+ + * | EMPTY | | EMPTY | + * | Grid | | CBX | + * | [0][x] | | | + * +--------+ +-------+ + * +--------+ +--------+ + * | EMPTY | | SB | + * | CBX | | [0][x] | + * +--------+ +--------+ + * + */ + for (size_t ix = grids.width() - 2; ix >= 1; --ix) { + io_coords[TOP].push_back(vtr::Point(ix, grids.height() - 1)); + } + io_coords[TOP].push_back(vtr::Point(0, grids.height() - 1)); + + /* LEFT side I/Os */ + for (size_t iy = grids.height() - 2; iy >= 1; --iy) { + io_coords[LEFT].push_back(vtr::Point(0, iy)); + } + + for (const e_side& io_side : io_sides) { + for (const vtr::Point& io_coord : io_coords[io_side]) { + /* Identify the GSB that surrounds the grid */ + organize_top_module_tile_memory_modules(module_manager, top_module, + circuit_lib, sram_orgz_type, sram_model, + grids, grid_instance_ids, + device_rr_gsb, sb_instance_ids, cb_instance_ids, + compact_routing_hierarchy, + io_coord, io_side); + } + } + + /* For the core grids */ + std::vector> core_coords; + bool positive_direction = true; + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + /* For positive direction: -----> */ + if (true == positive_direction) { + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + core_coords.push_back(vtr::Point(ix, iy)); + } + } else { + VTR_ASSERT(false == positive_direction); + /* For negative direction: -----> */ + for (size_t ix = grids.width() - 2; ix >= 1; --ix) { + core_coords.push_back(vtr::Point(ix, iy)); + } + } + /* Flip the positive direction to be negative */ + positive_direction = !positive_direction; + } + + for (const vtr::Point& core_coord : core_coords) { + organize_top_module_tile_memory_modules(module_manager, top_module, + circuit_lib, sram_orgz_type, sram_model, + grids, grid_instance_ids, + device_rr_gsb, sb_instance_ids, cb_instance_ids, + compact_routing_hierarchy, + core_coord, NUM_SIDES); + } +} + + +/********************************************************************* + * Add the port-to-port connection between all the memory modules + * and their parent module + * + * Create nets to wire the control signals of memory module to + * the configuration ports of primitive module + * + * Configuration Chain + * ------------------- + * + * config_bus (head) config_bus (tail) + * | ^ + * primitive | | + * +---------------------------------------------+ + * | | | | + * | v | | + * | +-------------------------------------+ | + * | | CMOS-based Memory Modules | | + * | +-------------------------------------+ | + * | | | | + * | v v | + * | sram_out sram_outb | + * | | + * +---------------------------------------------+ + * + * Memory bank + * ----------- + * + * config_bus (BL) config_bus (WL) + * | | + * primitive | | + * +---------------------------------------------+ + * | | | | + * | v v | + * | +-------------------------------------+ | + * | | CMOS-based Memory Modules | | + * | +-------------------------------------+ | + * | | | | + * | v v | + * | sram_out sram_outb | + * | | + * +---------------------------------------------+ + * + **********************************************************************/ +static +void add_top_module_nets_cmos_memory_config_bus(ModuleManager& module_manager, + const ModuleId& parent_module, + const e_config_protocol_type& sram_orgz_type) { + switch (sram_orgz_type) { + case CONFIG_MEM_STANDALONE: + /* Nothing to do */ + break; + case CONFIG_MEM_SCAN_CHAIN: { + add_module_nets_cmos_memory_chain_config_bus(module_manager, parent_module, CONFIG_MEM_SCAN_CHAIN); + break; + } + case CONFIG_MEM_MEMORY_BANK: + /* TODO: */ + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid type of SRAM organization!\n"); + exit(1); + } +} + + +/******************************************************************** + * TODO: + * Add the port-to-port connection between a memory module + * and the configuration bus of a primitive module + * + * Create nets to wire the control signals of memory module to + * the configuration ports of primitive module + * + * Primitive module + * +----------------------------+ + * | +--------+ | + * config | | | | + * ports --->|--------------->| Memory | | + * | | Module | | + * | | | | + * | +--------+ | + * +----------------------------+ + * The detailed config ports really depend on the type + * of SRAM organization. + * + * The config_bus in the argument is the reserved address of configuration + * bus in the parent_module for this memory module + * + * The configuration bus connection will depend not only + * the design technology of the memory cells but also the + * configuration styles of FPGA fabric. + * Here we will branch on the design technology + * + * Note: this function SHOULD be called after the pb_type_module is created + * and its child module (logic_module and memory_module) is created! + *******************************************************************/ +void add_top_module_nets_memory_config_bus(ModuleManager& module_manager, + const ModuleId& parent_module, + const e_config_protocol_type& sram_orgz_type, + const e_circuit_model_design_tech& mem_tech) { + switch (mem_tech) { + case CIRCUIT_MODEL_DESIGN_CMOS: + add_top_module_nets_cmos_memory_config_bus(module_manager, parent_module, + sram_orgz_type); + break; + case CIRCUIT_MODEL_DESIGN_RRAM: + /* TODO: */ + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid type of memory design technology!\n"); + exit(1); + } +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_memory.h b/openfpga/src/fabric/build_top_module_memory.h new file mode 100644 index 000000000..dfa9a5c78 --- /dev/null +++ b/openfpga/src/fabric/build_top_module_memory.h @@ -0,0 +1,43 @@ +#ifndef BUILD_TOP_MODULE_MEMORY_H +#define BUILD_TOP_MODULE_MEMORY_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ + +#include +#include +#include "vtr_ndmatrix.h" +#include "module_manager.h" +#include "circuit_types.h" +#include "circuit_library.h" +#include "device_grid.h" +#include "device_rr_gsb.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void organize_top_module_memory_modules(ModuleManager& module_manager, + const ModuleId& top_module, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, + const DeviceGrid& grids, + const vtr::Matrix& grid_instance_ids, + const DeviceRRGSB& device_rr_gsb, + const vtr::Matrix& sb_instance_ids, + const std::map>& cb_instance_ids, + const bool& compact_routing_hierarchy); + +void add_top_module_nets_memory_config_bus(ModuleManager& module_manager, + const ModuleId& parent_module, + const e_config_protocol_type& sram_orgz_type, + const e_circuit_model_design_tech& mem_tech); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fabric/build_top_module_utils.cpp b/openfpga/src/fabric/build_top_module_utils.cpp new file mode 100644 index 000000000..141d2dd54 --- /dev/null +++ b/openfpga/src/fabric/build_top_module_utils.cpp @@ -0,0 +1,90 @@ +/******************************************************************** + * This file include most utilized functions for building the module + * graph for FPGA fabric + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" + +/* Headers from vpr library */ +#include "vpr_utils.h" + +#include "openfpga_naming.h" + +/* Module builder headers */ +#include "build_top_module_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Generate the name for a grid block, by considering + * 1. if it locates on the border with given device size + * 2. its type + * + * This function is mainly used in the top-level module generation + *******************************************************************/ +std::string generate_grid_block_module_name_in_top_module(const std::string& prefix, + const DeviceGrid& grids, + const vtr::Point& grid_coord) { + /* Determine if the grid locates at the border */ + vtr::Point device_size(grids.width(), grids.height()); + e_side border_side = find_grid_border_side(device_size, grid_coord); + + return generate_grid_block_module_name(prefix, + std::string(grids[grid_coord.x()][grid_coord.y()].type->name), + is_io_type(grids[grid_coord.x()][grid_coord.y()].type), + border_side); +} + +/******************************************************************** + * Find the cb_type of a GSB in the top-level module + * depending on the side of SB + * TOP/BOTTOM side: CHANY + * RIGHT/LEFT side: CHANX + *******************************************************************/ +t_rr_type find_top_module_cb_type_by_sb_side(const e_side& sb_side) { + VTR_ASSERT(NUM_SIDES != sb_side); + + if ((TOP == sb_side) || (BOTTOM == sb_side)) { + return CHANY; + } + + VTR_ASSERT((RIGHT == sb_side) || (LEFT == sb_side)); + return CHANX; +} + +/******************************************************************** + * Find the GSB coordinate for a CB in the top-level module + * depending on the side of a SB + * TODO: use vtr::Point to replace DeviceCoordinator + *******************************************************************/ +vtr::Point find_top_module_gsb_coordinate_by_sb_side(const RRGSB& rr_gsb, + const e_side& sb_side) { + VTR_ASSERT(NUM_SIDES != sb_side); + + vtr::Point gsb_coordinate; + + if ((TOP == sb_side) || (LEFT == sb_side)) { + gsb_coordinate.set_x(rr_gsb.get_x()); + gsb_coordinate.set_y(rr_gsb.get_y()); + return gsb_coordinate; + } + + VTR_ASSERT((RIGHT == sb_side) || (BOTTOM == sb_side)); + + /* RIGHT side: x + 1 */ + if (RIGHT == sb_side) { + gsb_coordinate.set_x(rr_gsb.get_x() + 1); + gsb_coordinate.set_y(rr_gsb.get_y()); + } + + /* BOTTOM side: y - 1 */ + if (BOTTOM == sb_side) { + gsb_coordinate.set_x(rr_gsb.get_x()); + gsb_coordinate.set_y(rr_gsb.get_y() - 1); + } + + return gsb_coordinate; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_top_module_utils.h b/openfpga/src/fabric/build_top_module_utils.h new file mode 100644 index 000000000..c0bcbcee5 --- /dev/null +++ b/openfpga/src/fabric/build_top_module_utils.h @@ -0,0 +1,31 @@ +#ifndef BUILD_TOP_MODULE_UTILS_H +#define BUILD_TOP_MODULE_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "vtr_geometry.h" +#include "device_grid.h" +#include "rr_gsb.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +std::string generate_grid_block_module_name_in_top_module(const std::string& prefix, + const DeviceGrid& grids, + const vtr::Point& grid_coord); + +t_rr_type find_top_module_cb_type_by_sb_side(const e_side& sb_side); + +vtr::Point find_top_module_gsb_coordinate_by_sb_side(const RRGSB& rr_gsb, + const e_side& sb_side); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/tile_direct/build_tile_direct.cpp b/openfpga/src/tile_direct/build_tile_direct.cpp new file mode 100644 index 000000000..5a40e2ca5 --- /dev/null +++ b/openfpga/src/tile_direct/build_tile_direct.cpp @@ -0,0 +1,367 @@ +/*************************************************************************************** + * This file includes functions that build the point-to-point direct connections + * between tiles (programmable blocks) + ***************************************************************************************/ + +/* Headers from vtrutil library */ +#include "vtr_log.h" +#include "vtr_assert.h" +#include "vtr_time.h" + +/* Headers from openfpgautil library */ +#include "openfpga_tokenizer.h" +#include "openfpga_port.h" +#include "openfpga_port_parser.h" + +/* Headers from vpr library */ +#include "vpr_utils.h" + +#include "build_tile_direct.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/*************************************************************************************** + * Parse the from tile name from the direct definition + * The definition string should be in the following format: + * .[:] + ***************************************************************************************/ +static +std::string parse_direct_tile_name(const std::string& direct_tile_inf) { + StringToken tokenizer(direct_tile_inf); + std::vector tokens = tokenizer.split('.'); + /* We should have only 2 elements and the first is tile name */ + if (2 != tokens.size()) { + VTR_LOG_ERROR("Invalid definition on direct tile '%s'!\n\tExpect .[:].\n", + direct_tile_inf.c_str()); + } + + return tokens[0]; +} + +/*************************************************************************************** + * Check if a pin is located on a given side of physical tile + * If the given side is NUM_SIDES, we will search all the sides + ***************************************************************************************/ +static +bool is_pin_locate_at_physical_tile_side(t_physical_tile_type_ptr physical_tile, + const size_t& pin_width_offset, + const size_t& pin_height_offset, + const size_t& pin_id, + const e_side& pin_side) { + if (NUM_SIDES == pin_side) { + for (size_t side = 0; side < NUM_SIDES; ++side) { + if (true == physical_tile->pinloc[pin_width_offset][pin_height_offset][side][pin_id]) { + return true; + } + } + } + + return physical_tile->pinloc[pin_width_offset][pin_height_offset][size_t(pin_side)][pin_id]; +} + +/*************************************************************************************** + * Find the pin ids of a physical tile based on the given port name, LSB and MSB + ***************************************************************************************/ +static +std::vector find_physical_tile_pin_id(t_physical_tile_type_ptr physical_tile, + const size_t& pin_width_offset, + const size_t& pin_height_offset, + const BasicPort& tile_port, + const e_side& pin_side) { + std::vector pin_ids; + + /* Walk through the port of the tile */ + for (const t_physical_tile_port& physical_tile_port : physical_tile->ports) { + if (std::string(physical_tile_port.name) != tile_port.get_name()) { + continue; + } + /* If the wanted port is invalid, it assumes that we want the full port */ + if (false == tile_port.is_valid()) { + for (int ipin = 0; ipin < physical_tile_port.num_pins; ++ipin) { + int pin_id = physical_tile_port.absolute_first_pin_index + ipin; + VTR_ASSERT(pin_id < physical_tile->num_pins); + /* Check if the pin is located on the wanted side */ + if (true == is_pin_locate_at_physical_tile_side(physical_tile, + pin_width_offset, + pin_height_offset, + pin_id, pin_side)) { + pin_ids.push_back(pin_id); + } + } + continue; + } + /* Find the LSB and MSB of the pin */ + VTR_ASSERT_SAFE(true == tile_port.is_valid()); + BasicPort ref_port(physical_tile_port.name, physical_tile_port.num_pins); + if (false == ref_port.contained(tile_port)) { + VTR_LOG_ERROR("Defined direct port '%s[%lu:%lu]' is out of range for physical port '%s[%lu:%lu]'!\n", + tile_port.get_name().c_str(), + tile_port.get_lsb(), tile_port.get_msb(), + ref_port.get_name().c_str(), + ref_port.get_lsb(), ref_port.get_msb()); + exit(1); + } + for (const size_t& ipin : tile_port.pins()) { + int pin_id = physical_tile_port.absolute_first_pin_index + ipin; + VTR_ASSERT(pin_id < physical_tile->num_pins); + /* Check if the pin is located on the wanted side */ + if (true == is_pin_locate_at_physical_tile_side(physical_tile, + pin_width_offset, + pin_height_offset, + pin_id, pin_side)) { + + pin_ids.push_back(pin_id); + } + } + } + + return pin_ids; +} + +/*************************************************************************************** + * Build the point-to-point direct connections based on + * - original VPR arch definition + * This is limited to the inner-column and inner-row connections + * + * Build the inner-column and inner-row connections + * + * +------+ + * | Tile | + * +------+ +------+ +------+ + * | or | Tile |--->| Tile | + * v +------+ +------+ + * +------+ + * | Tile | + * +------+ + * + ***************************************************************************************/ +static +void build_inner_column_row_tile_direct(TileDirect& tile_direct, + t_direct_inf& vpr_direct, + const DeviceContext& device_ctx, + const ArchDirectId& arch_direct_id) { + /* Get the source tile and pin information */ + std::string from_tile_name = parse_direct_tile_name(std::string(vpr_direct.from_pin)); + PortParser from_tile_port_parser(std::string(vpr_direct.from_pin)); + const BasicPort& from_tile_port = from_tile_port_parser.port(); + + /* Get the sink tile and pin information */ + std::string to_tile_name = parse_direct_tile_name(std::string(vpr_direct.to_pin)); + PortParser to_tile_port_parser(std::string(vpr_direct.to_pin)); + const BasicPort& to_tile_port = to_tile_port_parser.port(); + + /* Walk through the device fabric and find the grid that fit the source */ + for (size_t x = 0; x < device_ctx.grid.width(); ++x) { + for (size_t y = 0; y < device_ctx.grid.height(); ++y) { + /* Bypass empty grid */ + if (true == is_empty_type(device_ctx.grid[x][y].type)) { + continue; + } + + /* Bypass the grid that does not fit the from_tile name */ + if (from_tile_name != std::string(device_ctx.grid[x][y].type->name)) { + continue; + } + + /* Try to find the pin in this tile */ + std::vector from_pins = find_physical_tile_pin_id(device_ctx.grid[x][y].type, + device_ctx.grid[x][y].width_offset, + device_ctx.grid[x][y].height_offset, + from_tile_port, + vpr_direct.from_side); + /* If nothing found, we can continue */ + if (0 == from_pins.size()) { + continue; + } + + /* We should try to the sink grid for inner-column/row direct connections */ + vtr::Point from_grid_coord(x, y); + vtr::Point to_grid_coord(x + vpr_direct.x_offset, y + vpr_direct.y_offset); + if ((to_grid_coord.x() >= device_ctx.grid.width()) + || (to_grid_coord.y() >= device_ctx.grid.height())) { + continue; + } + + /* Bypass the grid that does not fit the from_tile name */ + if (to_tile_name != std::string(device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].type->name)) { + continue; + } + /* Try to find the pin in this tile */ + std::vector to_pins = find_physical_tile_pin_id(device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].type, + device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].width_offset, + device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].height_offset, + to_tile_port, + vpr_direct.to_side); + /* If nothing found, we can continue */ + if (0 == to_pins.size()) { + continue; + } + + /* If from port and to port do not match in sizes, error out */ + if (from_pins.size() != to_pins.size()) { + VTR_LOG_ERROR("From_port '%s[%lu:%lu] of direct '%s' does not match to_port '%s[%lu:%lu]'!\n", + from_tile_port.get_name().c_str(), + from_tile_port.get_lsb(), + from_tile_port.get_msb(), + vpr_direct.name, + to_tile_port.get_name().c_str(), + to_tile_port.get_lsb(), + to_tile_port.get_msb()); + exit(1); + } + + /* Now add the tile direct */ + for (size_t ipin = 0; ipin < from_pins.size(); ++ipin) { + TileDirectId tile_direct_id = tile_direct.add_direct(device_ctx.grid[x][y].type, + from_grid_coord, vpr_direct.from_side, from_pins[ipin], + device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].type, + to_grid_coord, vpr_direct.to_side, to_pins[ipin]); + tile_direct.set_arch_direct_id(tile_direct_id, arch_direct_id); + } + + } + } +} + +/*************************************************************************************** + * Build the point-to-point direct connections based on + * - OpenFPGA arch definition + * This is limited to the inter-column and inter-row connections + * + * Build the inter-column and inter-row connections + * + * +------+ +------+ + * | Tile | | Tile | + * +------+ +------+ + * | ^ + * | | + * +------------- + * + * +------+ + * | Tile |-------+ + * +------+ | + * | + * +------+ | + * | Tile |<------+ + * +------+ + * + * + ***************************************************************************************/ +static +void build_inter_column_row_tile_direct(TileDirect& tile_direct, + t_direct_inf& vpr_direct, + const DeviceContext& device_ctx, + const ArchDirect& arch_direct, + const ArchDirectId& arch_direct_id, + const CircuitLibrary& circuit_lib) { + /* Get the source tile and pin information */ + std::string from_tile_name = parse_direct_tile_name(std::string(vpr_direct.from_pin)); + PortParser from_tile_port_parser(std::string(vpr_direct.from_pin)); + const BasicPort& from_tile_port = from_tile_port_parser.port(); + + /* Get the sink tile and pin information */ + std::string to_tile_name = parse_direct_tile_name(std::string(vpr_direct.to_pin)); + PortParser to_tile_port_parser(std::string(vpr_direct.to_pin)); + const BasicPort& to_tile_port = to_tile_port_parser.port(); + + /* Walk through the device fabric and find the grid that fit the source */ + for (size_t x = 0; x < device_ctx.grid.width(); ++x) { + for (size_t y = 0; y < device_ctx.grid.height(); ++y) { + /* Bypass empty grid */ + if (true == is_empty_type(device_ctx.grid[x][y].type)) { + continue; + } + + /* Bypass the grid that does not fit the from_tile name */ + if (from_tile_name != std::string(device_ctx.grid[x][y].type->name)) { + continue; + } + + /* Try to find the pin in this tile */ + std::vector from_pins = find_physical_tile_pin_id(device_ctx.grid[x][y].type, + device_ctx.grid[x][y].width_offset, + device_ctx.grid[x][y].height_offset, + from_tile_port, + vpr_direct.from_side); + /* If nothing found, we can continue */ + if (0 == from_pins.size()) { + continue; + } + + /* We should try to the sink grid for inner-column/row direct connections */ + vtr::Point from_grid_coord(x, y); + vtr::Point to_grid_coord(x + vpr_direct.x_offset, y + vpr_direct.y_offset); + if ((to_grid_coord.x() >= device_ctx.grid.width()) + || (to_grid_coord.y() >= device_ctx.grid.height())) { + continue; + } + + /* Bypass the grid that does not fit the from_tile name */ + if (to_tile_name != std::string(device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].type->name)) { + continue; + } + /* Try to find the pin in this tile */ + std::vector to_pins = find_physical_tile_pin_id(device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].type, + device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].width_offset, + device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].height_offset, + to_tile_port, + vpr_direct.to_side); + /* If nothing found, we can continue */ + if (0 == to_pins.size()) { + continue; + } + + /* If from port and to port do not match in sizes, error out */ + if (from_pins.size() != to_pins.size()) { + VTR_LOG_ERROR("From_port '%s[%lu:%lu] of direct '%s' does not match to_port '%s[%lu:%lu]'!\n", + from_tile_port.get_name().c_str(), + from_tile_port.get_lsb(), + from_tile_port.get_msb(), + vpr_direct.name, + to_tile_port.get_name().c_str(), + to_tile_port.get_lsb(), + to_tile_port.get_msb()); + exit(1); + } + + /* Now add the tile direct */ + for (size_t ipin = 0; ipin < from_pins.size(); ++ipin) { + TileDirectId tile_direct_id = tile_direct.add_direct(device_ctx.grid[x][y].type, + from_grid_coord, vpr_direct.from_side, from_pins[ipin], + device_ctx.grid[to_grid_coord.x()][to_grid_coord.y()].type, + to_grid_coord, vpr_direct.to_side, to_pins[ipin]); + tile_direct.set_arch_direct_id(tile_direct_id, arch_direct_id); + } + + } + } +} + +/*************************************************************************************** + * Top-level functions that build the point-to-point direct connections + * between tiles (programmable blocks) + ***************************************************************************************/ +TileDirect build_device_tile_direct(const DeviceContext& device_ctx, + const ArchDirect& arch_direct, + const CircuitLibrary& circuit_lib) { + vtr::ScopedStartFinishTimer timer("Build the annotation about direct connection between tiles"); + + TileDirect tile_direct; + + /* Walk through each direct definition in the VPR arch */ + for (int idirect = 0; idirect < device_ctx.arch->num_directs; ++idirect) { + ArchDirectId arch_direct_id = arch_direct.direct(std::string(device_ctx.arch->Directs[idirect].name)); + VTR_ASSERT(ArchDirectId::INVALID() != arch_direct_id); + /* Build from original VPR arch definition */ + build_inner_column_row_tile_direct(tile_direct, + device_ctx.arch->Directs[idirect], + device_ctx, + arch_direct_id); + /* TODO: Build from OpenFPGA arch definition */ + } + + return tile_direct; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/tile_direct/build_tile_direct.h b/openfpga/src/tile_direct/build_tile_direct.h new file mode 100644 index 000000000..08d0397ce --- /dev/null +++ b/openfpga/src/tile_direct/build_tile_direct.h @@ -0,0 +1,25 @@ +#ifndef BUILD_TILE_DIRECT_H +#define BUILD_TILE_DIRECT_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "vpr_context.h" +#include "arch_direct.h" +#include "tile_direct.h" +#include "circuit_library.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +TileDirect build_device_tile_direct(const DeviceContext& device_ctx, + const ArchDirect& arch_direct, + const CircuitLibrary& circuit_lib); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/tile_direct/tile_direct.cpp b/openfpga/src/tile_direct/tile_direct.cpp new file mode 100644 index 000000000..d0d1f6122 --- /dev/null +++ b/openfpga/src/tile_direct/tile_direct.cpp @@ -0,0 +1,117 @@ +/****************************************************************************** + * Memember functions for data structure TileDirect + ******************************************************************************/ +#include "vtr_assert.h" + +#include "tile_direct.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/************************************************** + * Public Accessors + *************************************************/ +TileDirect::tile_direct_range TileDirect::directs() const { + return vtr::make_range(direct_ids_.begin(), direct_ids_.end()); +} + +t_physical_tile_type_ptr TileDirect::from_tile(const TileDirectId& direct_id) const { + /* Validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return from_tiles_[direct_id]; +} + +vtr::Point TileDirect::from_tile_coordinate(const TileDirectId& direct_id) const { + /* Validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return from_tile_coords_[direct_id]; +} + +size_t TileDirect::from_tile_pin(const TileDirectId& direct_id) const { + /* Validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return from_tile_pins_[direct_id]; +} + +e_side TileDirect::from_tile_side(const TileDirectId& direct_id) const { + /* Validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return from_tile_sides_[direct_id]; +} + +t_physical_tile_type_ptr TileDirect::to_tile(const TileDirectId& direct_id) const { + /* Validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return to_tiles_[direct_id]; +} + +vtr::Point TileDirect::to_tile_coordinate(const TileDirectId& direct_id) const { + /* Validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return to_tile_coords_[direct_id]; +} + +size_t TileDirect::to_tile_pin(const TileDirectId& direct_id) const { + /* Validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return to_tile_pins_[direct_id]; +} + +e_side TileDirect::to_tile_side(const TileDirectId& direct_id) const { + /* Validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return to_tile_sides_[direct_id]; +} + +ArchDirectId TileDirect::arch_direct(const TileDirectId& direct_id) const { + /* Validate the direct_id */ + VTR_ASSERT(valid_direct_id(direct_id)); + return arch_directs_[direct_id]; +} + +/****************************************************************************** + * Private Mutators + ******************************************************************************/ +TileDirectId TileDirect::add_direct(t_physical_tile_type_ptr from_tile, + const vtr::Point& from_tile_coord, + const e_side& from_tile_side, + const size_t& from_tile_pin, + t_physical_tile_type_ptr to_tile, + const vtr::Point& to_tile_coord, + const e_side& to_tile_side, + const size_t& to_tile_pin) { + /* Create an new id */ + TileDirectId direct = TileDirectId(direct_ids_.size()); + direct_ids_.push_back(direct); + + /* Allocate other attributes */ + from_tiles_.push_back(from_tile); + from_tile_coords_.push_back(from_tile_coord); + from_tile_sides_.push_back(from_tile_side); + from_tile_pins_.push_back(from_tile_pin); + + to_tiles_.push_back(to_tile); + to_tile_coords_.push_back(to_tile_coord); + to_tile_sides_.push_back(to_tile_side); + to_tile_pins_.push_back(to_tile_pin); + + arch_directs_.emplace_back(ArchDirectId::INVALID()); + + return direct; +} + +void TileDirect::set_arch_direct_id(const TileDirectId& tile_direct_id, + const ArchDirectId& arch_direct_id) { + /* Validate the direct_id */ + VTR_ASSERT(valid_direct_id(tile_direct_id)); + arch_directs_[tile_direct_id] = arch_direct_id; +} + +/****************************************************************************** + * Private validators/invalidators + ******************************************************************************/ +bool TileDirect::valid_direct_id(const TileDirectId& direct_id) const { + return ( size_t(direct_id) < direct_ids_.size() ) && ( direct_id == direct_ids_[direct_id] ); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/tile_direct/tile_direct.h b/openfpga/src/tile_direct/tile_direct.h new file mode 100644 index 000000000..d8d0e180f --- /dev/null +++ b/openfpga/src/tile_direct/tile_direct.h @@ -0,0 +1,86 @@ +#ifndef TILE_DIRECT_H +#define TILE_DIRECT_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_geometry.h" +#include "vtr_vector.h" + +/* Headers from readarch library */ +#include "physical_types.h" + +/* Headers from readarchopenfpga library */ +#include "arch_direct.h" + +#include "tile_direct_fwd.h" + +/* Begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * TileDirect object aims to be a database to store all the information + * about direct connection between tiles + * - starting tile and end tile for each point-to-point direct connection + * - circuit model to implement each direct connection + * + * TileDirect is compiled from ArchDirect for a specific FPGA fabric. + *******************************************************************/ +class TileDirect { + public: /* Types and ranges */ + typedef vtr::vector::const_iterator tile_direct_iterator; + typedef vtr::Range tile_direct_range; + public: /* Public aggregators */ + tile_direct_range directs() const; + t_physical_tile_type_ptr from_tile(const TileDirectId& direct_id) const; + vtr::Point from_tile_coordinate(const TileDirectId& direct_id) const; + e_side from_tile_side(const TileDirectId& direct_id) const; + size_t from_tile_pin(const TileDirectId& direct_id) const; + t_physical_tile_type_ptr to_tile(const TileDirectId& direct_id) const; + vtr::Point to_tile_coordinate(const TileDirectId& direct_id) const; + e_side to_tile_side(const TileDirectId& direct_id) const; + size_t to_tile_pin(const TileDirectId& direct_id) const; + ArchDirectId arch_direct(const TileDirectId& direct_id) const; + public: /* Public mutators */ + TileDirectId add_direct(t_physical_tile_type_ptr from_tile, + const vtr::Point& from_tile_coord, + const e_side& from_tile_side, + const size_t& from_tile_pin, + t_physical_tile_type_ptr to_tile, + const vtr::Point& to_tile_coord, + const e_side& to_tile_side, + const size_t& to_tile_pin); + void set_arch_direct_id(const TileDirectId& tile_direct_id, + const ArchDirectId& arch_direct_id); + public: /* Public validators/invalidators */ + bool valid_direct_id(const TileDirectId& direct_id) const; + private: /* Internal Data */ + vtr::vector direct_ids_; + + /* Detailed information about the starting tile + * - tile type description + * - tile coordinate + * - tile pin id + */ + vtr::vector from_tiles_; + vtr::vector> from_tile_coords_; + vtr::vector from_tile_sides_; + vtr::vector from_tile_pins_; + + /* Detailed information about the ending tile + * - tile type description + * - tile coordinate + * - tile pin id + */ + vtr::vector to_tiles_; + vtr::vector> to_tile_coords_; + vtr::vector to_tile_sides_; + vtr::vector to_tile_pins_; + + vtr::vector arch_directs_; +}; + +} /* End namespace openfpga*/ + +#endif diff --git a/openfpga/src/tile_direct/tile_direct_fwd.h b/openfpga/src/tile_direct/tile_direct_fwd.h new file mode 100644 index 000000000..c83a00ee9 --- /dev/null +++ b/openfpga/src/tile_direct/tile_direct_fwd.h @@ -0,0 +1,23 @@ +/************************************************** + * This file includes only declarations for + * the data structures for TileDirect + * Please refer to tile_direct.h for more details + *************************************************/ +#ifndef TILE_DIRECT_FWD_H +#define TILE_DIRECT_FWD_H + +#include "vtr_strong_id.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/* Strong Ids for ModuleManager */ +struct tile_direct_id_tag; + +typedef vtr::StrongId TileDirectId; + +class TileDirect; + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 25a77a752..9797763b0 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -10,6 +10,9 @@ #include "vtr_log.h" #include "vtr_assert.h" +/* Headers from openfpgautil library */ +#include "openfpga_port.h" + #include "openfpga_naming.h" #include "memory_utils.h" #include "pb_type_utils.h" diff --git a/openfpga/src/utils/rr_gsb_utils.cpp b/openfpga/src/utils/rr_gsb_utils.cpp new file mode 100644 index 000000000..3f332e6db --- /dev/null +++ b/openfpga/src/utils/rr_gsb_utils.cpp @@ -0,0 +1,39 @@ +/******************************************************************** + * This file includes most utilized functions for data structure + * DeviceRRGSB + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" + +/* Headers from openfpgautil library */ +#include "openfpga_side_manager.h" + +#include "rr_gsb_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Find if a X-direction or Y-direction Connection Block contains + * routing tracks only (zero configuration bits and routing multiplexers) + *******************************************************************/ +bool connection_block_contain_only_routing_tracks(const RRGSB& rr_gsb, + const t_rr_type& cb_type) { + bool routing_track_only = true; + + /* Find routing multiplexers on the sides of a Connection block where IPIN nodes locate */ + std::vector cb_sides = rr_gsb.get_cb_ipin_sides(cb_type); + + for (size_t side = 0; side < cb_sides.size(); ++side) { + enum e_side cb_ipin_side = cb_sides[side]; + SideManager side_manager(cb_ipin_side); + if (0 < rr_gsb.get_num_ipin_nodes(cb_ipin_side)) { + routing_track_only = false; + break; + } + } + + return routing_track_only; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/utils/rr_gsb_utils.h b/openfpga/src/utils/rr_gsb_utils.h new file mode 100644 index 000000000..57e3e23fc --- /dev/null +++ b/openfpga/src/utils/rr_gsb_utils.h @@ -0,0 +1,23 @@ +#ifndef RR_GSB_UTILS_H +#define RR_GSB_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "rr_gsb.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +bool connection_block_contain_only_routing_tracks(const RRGSB& rr_gsb, + const t_rr_type& cb_type); + +} /* end namespace openfpga */ + +#endif