diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 36901e6f0..503427303 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -162,7 +162,8 @@ enum e_pin_type { enum e_interconnect { COMPLETE_INTERC = 1, DIRECT_INTERC = 2, - MUX_INTERC = 3 + MUX_INTERC = 3, + NUM_INTERC_TYPES }; /* Orientations. */ diff --git a/openfpga/src/base/annotate_pb_graph.cpp b/openfpga/src/base/annotate_pb_graph.cpp new file mode 100644 index 000000000..82e214067 --- /dev/null +++ b/openfpga/src/base/annotate_pb_graph.cpp @@ -0,0 +1,122 @@ +/******************************************************************** + * This file includes functions that are used to annotate pb_graph_node + * and pb_graph_pins from VPR to OpenFPGA + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "pb_type_utils.h" +#include "pb_graph_utils.h" + +#include "annotate_pb_graph.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * This function will recursively walk through all the pb_graph nodes + * starting from a top node. + * It aims to annotate the physical type of each interconnect. + * This is due to that the type of interconnect 'complete' may diverge + * in physical implmentation. + * - When there is only one input driving a 'complete' interconnection, + * it will be implemented with wires + * - When there are multiple inputs driving a 'complete' interconnection, + * it will be implemented with routing multiplexers + *******************************************************************/ +static +void rec_build_vpr_pb_graph_interconnect_physical_type_annotation(t_pb_graph_node* pb_graph_node, + VprPbTypeAnnotation& vpr_pb_type_annotation) { + /* Skip the root node because we start from the inputs of child pb_graph node + * + * pb_graph_node + * +---------------------------------- + * | child_pb_graph_node + * | +----------------- + * | | + * |-------------+-->input_pins + * | | + * |-------------+-->clock_pins + * | + * + */ + if (false == pb_graph_node->is_root()) { + /* We only care the physical modes! But we have to find it through the parent node */ + t_mode* child_physical_mode = vpr_pb_type_annotation.physical_mode(pb_graph_node->parent_pb_graph_node->pb_type); + VTR_ASSERT(nullptr != child_physical_mode); + + std::map interc_num_inputs; + /* Initialize the counter */ + for (t_interconnect* interc : pb_mode_interconnects(child_physical_mode)) { + interc_num_inputs[interc] = 0; + } + + /* We only care input and clock pins */ + for (int iport = 0; iport < pb_graph_node->num_input_ports; ++iport) { + for (int ipin = 0; ipin < pb_graph_node->num_input_pins[iport]; ++ipin) { + /* For each interconnect, we count the total number of inputs */ + for (t_interconnect* interc : pb_mode_interconnects(child_physical_mode)) { + interc_num_inputs[interc] += pb_graph_pin_inputs(&(pb_graph_node->input_pins[iport][ipin]), interc).size(); + } + } + } + + for (int iport = 0; iport < pb_graph_node->num_clock_ports; ++iport) { + for (int ipin = 0; ipin < pb_graph_node->num_clock_pins[iport]; ++ipin) { + /* For each interconnect, we count the total number of inputs */ + for (t_interconnect* interc : pb_mode_interconnects(child_physical_mode)) { + interc_num_inputs[interc] += pb_graph_pin_inputs(&(pb_graph_node->clock_pins[iport][ipin]), interc).size(); + } + } + } + + /* For each interconnect that has more than 1 input, we can infer the physical type */ + for (t_interconnect* interc : pb_mode_interconnects(child_physical_mode)) { + e_interconnect interc_physical_type = pb_interconnect_physical_type(interc, interc_num_inputs[interc]); + if (interc_physical_type == vpr_pb_type_annotation.interconnect_physical_type(interc)) { + /* Skip annotation if we have already done! */ + continue; + } + vpr_pb_type_annotation.add_interconnect_physical_type(interc, interc_physical_type); + } + } + + /* If we reach a primitive pb_graph node, we return */ + if (true == is_primitive_pb_type(pb_graph_node->pb_type)) { + return; + } + + /* Recursively visit all the child pb_graph_nodes */ + t_mode* physical_mode = vpr_pb_type_annotation.physical_mode(pb_graph_node->pb_type); + VTR_ASSERT(nullptr != physical_mode); + for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) { + /* Each child may exist multiple times in the hierarchy*/ + for (int jpb = 0; jpb < physical_mode->pb_type_children[ipb].num_pb; ++jpb) { + rec_build_vpr_pb_graph_interconnect_physical_type_annotation(&(pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][jpb]), vpr_pb_type_annotation); + } + } +} + +/******************************************************************** + * This function aims to annotate the physical type for each interconnect + * inside the pb_graph + * + * Note: + * - This function should be executed AFTER functions + * build_vpr_physical_pb_mode_explicit_annotation() + * build_vpr_physical_pb_mode_implicit_annotation() + *******************************************************************/ +void annotate_pb_graph_interconnect_physical_type(const DeviceContext& vpr_device_ctx, + VprPbTypeAnnotation& vpr_pb_type_annotation) { + for (const t_logical_block_type& lb_type : vpr_device_ctx.logical_block_types) { + /* By pass nullptr for pb_graph head */ + if (nullptr == lb_type.pb_graph_head) { + continue; + } + rec_build_vpr_pb_graph_interconnect_physical_type_annotation(lb_type.pb_graph_head, vpr_pb_type_annotation); + } +} + +} /* end namespace openfpga */ + diff --git a/openfpga/src/base/annotate_pb_graph.h b/openfpga/src/base/annotate_pb_graph.h new file mode 100644 index 000000000..8749cd28f --- /dev/null +++ b/openfpga/src/base/annotate_pb_graph.h @@ -0,0 +1,23 @@ +#ifndef ANNOTATE_PB_GRAPH_H +#define ANNOTATE_PB_GRAPH_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "vpr_context.h" +#include "openfpga_context.h" +#include "vpr_pb_type_annotation.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void annotate_pb_graph_interconnect_physical_type(const DeviceContext& vpr_device_ctx, + VprPbTypeAnnotation& vpr_pb_type_annotation); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/base/annotate_pb_types.cpp b/openfpga/src/base/annotate_pb_types.cpp index e8e7ddab4..c4605325c 100644 --- a/openfpga/src/base/annotate_pb_types.cpp +++ b/openfpga/src/base/annotate_pb_types.cpp @@ -9,6 +9,7 @@ #include "vpr_pb_type_annotation.h" #include "pb_type_utils.h" +#include "annotate_pb_graph.h" #include "annotate_pb_types.h" /* begin namespace openfpga */ @@ -843,6 +844,92 @@ void link_vpr_pb_interconnect_to_circuit_model_explicit_annotation(const DeviceC } } +/******************************************************************** + * This function will recursively visit all the pb_type from the top + * pb_type in the graph and infer the circuit model for physical mode + * of pb_types in VPR pb_type graph without OpenFPGA architecture XML + * + * Because only the interconnect in physical modes need circuit model + * annotation, we will skip all the operating modes here + * + * Note: + * - This function will automatically infer the type of circuit model + * that is required and find the default circuit model in the type + * - Interconnect type to Circuit mode type assumption: + * - MUX_INTERC -> CIRCUIT_MODEL_MUX + * - DIRECT_INTERC -> CIRCUIT_MODEL_WIRE + * - COMPLETE_INTERC (single input) -> CIRCUIT_MODEL_WIRE + * - COMPLETE_INTERC (multiple input pins) -> CIRCUIT_MODEL_MUX + *******************************************************************/ +static +void rec_infer_vpr_pb_interconnect_circuit_model_annotation(t_pb_type* cur_pb_type, + const CircuitLibrary& circuit_lib, + VprPbTypeAnnotation& vpr_pb_type_annotation) { + /* We do not check any primitive pb_type */ + if (true == is_primitive_pb_type(cur_pb_type)) { + return; + } + + /* Get the physical mode from annotation */ + t_mode* physical_mode = vpr_pb_type_annotation.physical_mode(cur_pb_type); + + VTR_ASSERT(nullptr != physical_mode); + + /* Annotate the circuit model for each interconnect under this physical mode */ + for (t_interconnect* pb_interc : pb_mode_interconnects(physical_mode)) { + /* If the interconnect has been annotated, we skip it */ + if (CircuitModelId::INVALID() != vpr_pb_type_annotation.interconnect_circuit_model(pb_interc)) { + continue; + } + /* Infer the circuit model type for a given interconnect */ + e_circuit_model_type circuit_model_type = pb_interconnect_require_circuit_model_type(vpr_pb_type_annotation.interconnect_physical_type(pb_interc)); + /* Try to find a default circuit model from the circuit library */ + CircuitModelId default_circuit_model = circuit_lib.default_model(circuit_model_type); + /* Update the annotation if the model id is valid */ + if (CircuitModelId::INVALID() == default_circuit_model) { + VTR_LOG_ERROR("Unable to infer a circuit model for interconnect '%s' under physical mode '%s' of pb_type '%s'!\n", + pb_interc->name, + physical_mode->name, + cur_pb_type->name); + } + vpr_pb_type_annotation.add_interconnect_circuit_model(pb_interc, default_circuit_model); + VTR_LOG("Implicitly infer a circuit model '%s' for interconnect '%s' under physical mode '%s' of pb_type '%s'\n", + circuit_lib.model_name(default_circuit_model).c_str(), + pb_interc->name, + physical_mode->name, + cur_pb_type->name); + } + + /* Traverse the pb_type children under the physical mode */ + for (int ichild = 0; ichild < physical_mode->num_pb_type_children; ++ichild) { + rec_infer_vpr_pb_interconnect_circuit_model_annotation(&(physical_mode->pb_type_children[ichild]), + circuit_lib, vpr_pb_type_annotation); + } +} + +/******************************************************************** + * This function will infer the circuit model for each interconnect + * under a physical mode of a pb_type in VPR pb_type graph without + * OpenFPGA architecture XML + * + * Note: + * This function must be executed AFTER the function + * build_vpr_physical_pb_mode_explicit_annotation() + * build_vpr_physical_pb_mode_implicit_annotation() + *******************************************************************/ +static +void link_vpr_pb_interconnect_to_circuit_model_implicit_annotation(const DeviceContext& vpr_device_ctx, + const CircuitLibrary& circuit_lib, + VprPbTypeAnnotation& vpr_pb_type_annotation) { + for (const t_logical_block_type& lb_type : vpr_device_ctx.logical_block_types) { + /* By pass nullptr for pb_type head */ + if (nullptr == lb_type.pb_type) { + continue; + } + rec_infer_vpr_pb_interconnect_circuit_model_annotation(lb_type.pb_type, circuit_lib, vpr_pb_type_annotation); + } +} + /******************************************************************** * Top-level function to link openfpga architecture to VPR, including: * - physical pb_type @@ -862,6 +949,13 @@ void annotate_pb_types(const DeviceContext& vpr_device_ctx, check_vpr_physical_pb_mode_annotation(vpr_device_ctx, const_cast(vpr_pb_type_annotation)); + /* Annotate the physical type for each interconnect under physical modes + * Must run AFTER physical mode annotation is done and + * BEFORE inferring the circuit model for interconnect + */ + annotate_pb_graph_interconnect_physical_type(vpr_device_ctx, + vpr_pb_type_annotation); + /* Annotate physical pb_types to operating pb_type in the VPR pb_type graph */ build_vpr_physical_pb_type_explicit_annotation(vpr_device_ctx, openfpga_arch, vpr_pb_type_annotation); @@ -876,10 +970,13 @@ void annotate_pb_types(const DeviceContext& vpr_device_ctx, * - physical pb_type to circuit model * - interconnect of physical pb_type to circuit model */ + /* TODO: link the pb_type port to circuit model port here! */ link_vpr_pb_type_to_circuit_model_explicit_annotation(vpr_device_ctx, openfpga_arch, vpr_pb_type_annotation); link_vpr_pb_interconnect_to_circuit_model_explicit_annotation(vpr_device_ctx, openfpga_arch, vpr_pb_type_annotation); + link_vpr_pb_interconnect_to_circuit_model_implicit_annotation(vpr_device_ctx, openfpga_arch.circuit_lib, + vpr_pb_type_annotation); /* Link physical pb_type to mode_bits */ diff --git a/openfpga/src/base/openfpga_link_arch.cpp b/openfpga/src/base/openfpga_link_arch.cpp index d10c5d931..a30453d1c 100644 --- a/openfpga/src/base/openfpga_link_arch.cpp +++ b/openfpga/src/base/openfpga_link_arch.cpp @@ -10,6 +10,7 @@ #include "vpr_pb_type_annotation.h" #include "pb_type_utils.h" #include "annotate_pb_types.h" +#include "annotate_pb_graph.h" #include "openfpga_link_arch.h" /* Include global variables of VPR */ diff --git a/openfpga/src/base/pb_graph_utils.cpp b/openfpga/src/base/pb_graph_utils.cpp new file mode 100644 index 000000000..84e642bde --- /dev/null +++ b/openfpga/src/base/pb_graph_utils.cpp @@ -0,0 +1,41 @@ +/******************************************************************** + * This file includes most utilized functions for the pb_graph_node + * and pb_graph_pin data structure in the OpenFPGA context + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "pb_graph_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * This function aims to find out all the pb_graph_pins that drive + * a given pb_graph pin w.r.t. a given interconnect definition + *******************************************************************/ +std::vector pb_graph_pin_inputs(t_pb_graph_pin* pb_graph_pin, + t_interconnect* selected_interconnect) { + std::vector inputs; + + /* Search the input edges only, stats on the size of MUX we may need (fan-in) */ + for (int iedge = 0; iedge < pb_graph_pin->num_input_edges; ++iedge) { + /* We care the only edges in the selected mode */ + if (selected_interconnect != pb_graph_pin->input_edges[iedge]->interconnect) { + continue; + } + for (int ipin = 0; ipin < pb_graph_pin->input_edges[iedge]->num_input_pins; ++ipin) { + /* Ensure that the pin is unique in the list */ + if (inputs.end() != std::find(inputs.begin(), inputs.end(), pb_graph_pin->input_edges[iedge]->input_pins[ipin])) { + continue; + } + /* Unique pin, push to the vector */ + inputs.push_back(pb_graph_pin->input_edges[iedge]->input_pins[ipin]); + } + } + + return inputs; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/base/pb_graph_utils.h b/openfpga/src/base/pb_graph_utils.h new file mode 100644 index 000000000..d46457b7d --- /dev/null +++ b/openfpga/src/base/pb_graph_utils.h @@ -0,0 +1,23 @@ +#ifndef PB_GRAPH_UTILS_H +#define PB_GRAPH_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "physical_types.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +std::vector pb_graph_pin_inputs(t_pb_graph_pin* pb_graph_pin, + t_interconnect* selected_interconnect); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/base/pb_type_utils.cpp b/openfpga/src/base/pb_type_utils.cpp index 93b644b8c..090661b50 100644 --- a/openfpga/src/base/pb_type_utils.cpp +++ b/openfpga/src/base/pb_type_utils.cpp @@ -1,7 +1,9 @@ /******************************************************************** - * This file includes most utilized functions for the pb_type - * and pb_graph_node data structure in the OpenFPGA context + * This file includes most utilized functions for the t_pb_type, + * t_mode and t_port data structure in the OpenFPGA context *******************************************************************/ +#include + /* Headers from vtrutil library */ #include "vtr_assert.h" #include "vtr_log.h" @@ -138,6 +140,18 @@ t_pb_type* try_find_pb_type_with_given_path(t_pb_type* top_pb_type, return nullptr; } +/******************************************************************** + * This function will return all the interconnects defined under a mode + * of pb_type + *******************************************************************/ +std::vector pb_mode_interconnects(t_mode* pb_mode) { + std::vector interc; + for (int i = 0; i < pb_mode->num_interconnect; ++i) { + interc.push_back(&(pb_mode->interconnect[i])); + } + return interc; +} + /******************************************************************** * This function will try to find an interconnect defined under a mode * of pb_type with a given name. @@ -154,4 +168,64 @@ t_interconnect* find_pb_mode_interconnect(t_mode* pb_mode, const char* interc_na return nullptr; } +/******************************************************************** + * This function will automatically infer the actual type of an interconnect + * that will be used to implement the physical design: + * - MUX_INTERC -> MUX_INTERC + * - DIRECT_INTERC -> DIRECT_INTERC + * - COMPLETE_INTERC (single input) -> DIRECT_INTERC + * - COMPLETE_INTERC (multiple input pins) -> MUX_INTERC + *******************************************************************/ +e_interconnect pb_interconnect_physical_type(t_interconnect* pb_interc, + const size_t& num_inputs) { + /* Check */ + VTR_ASSERT(nullptr != pb_interc); + + /* Initialize the interconnection type that will be implemented in SPICE netlist*/ + switch (pb_interc->type) { + case DIRECT_INTERC: + return DIRECT_INTERC; + break; + case COMPLETE_INTERC: + if (1 == num_inputs) { + return DIRECT_INTERC; + } else { + VTR_ASSERT(1 < num_inputs); + return MUX_INTERC; + } + break; + case MUX_INTERC: + return MUX_INTERC; + break; + default: + VTR_LOG_ERROR("Invalid type for interconnection '%s'!\n", + pb_interc->name); + exit(1); + } + + return NUM_INTERC_TYPES; +} + +/******************************************************************** + * This function will automatically infer the actual type of an interconnect + * that will be used to implement the physical design: + * - MUX_INTERC -> CIRCUIT_MODEL_MUX + * - DIRECT_INTERC -> CIRCUIT_MODEL_WIRE + * + * Note: + * - COMPLETE_INTERC should not appear here! + * - We assume the interconnect type is the physical type + * after interconnect physical type annotation is done! + *******************************************************************/ +e_circuit_model_type pb_interconnect_require_circuit_model_type(const e_interconnect& pb_interc_type) { + /* A map from interconnect type to circuit model type */ + std::map type_mapping; + type_mapping[MUX_INTERC] = CIRCUIT_MODEL_MUX; + type_mapping[DIRECT_INTERC] = CIRCUIT_MODEL_WIRE; + + VTR_ASSERT((MUX_INTERC == pb_interc_type) || (DIRECT_INTERC == pb_interc_type)); + + return type_mapping.at(pb_interc_type); +} + } /* end namespace openfpga */ diff --git a/openfpga/src/base/pb_type_utils.h b/openfpga/src/base/pb_type_utils.h index 3e9f9a07f..ac3ade343 100644 --- a/openfpga/src/base/pb_type_utils.h +++ b/openfpga/src/base/pb_type_utils.h @@ -7,6 +7,7 @@ #include #include #include "physical_types.h" +#include "circuit_library.h" /******************************************************************** * Function declaration @@ -31,8 +32,15 @@ t_pb_type* try_find_pb_type_with_given_path(t_pb_type* top_pb_type, const std::vector& target_pb_type_names, const std::vector& target_pb_mode_names); +std::vector pb_mode_interconnects(t_mode* pb_mode); + t_interconnect* find_pb_mode_interconnect(t_mode* pb_mode, const char* interc_name); +e_interconnect pb_interconnect_physical_type(t_interconnect* pb_interc, + const size_t& num_inputs); + +e_circuit_model_type pb_interconnect_require_circuit_model_type(const e_interconnect& pb_interc_type); + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/base/vpr_pb_type_annotation.cpp b/openfpga/src/base/vpr_pb_type_annotation.cpp index 8949d13c3..97dc7354b 100644 --- a/openfpga/src/base/vpr_pb_type_annotation.cpp +++ b/openfpga/src/base/vpr_pb_type_annotation.cpp @@ -85,6 +85,16 @@ CircuitModelId VprPbTypeAnnotation::interconnect_circuit_model(t_interconnect* p return interconnect_circuit_models_.at(pb_interconnect); } +e_interconnect VprPbTypeAnnotation::interconnect_physical_type(t_interconnect* pb_interconnect) const { + /* Ensure that the pb_type is in the list */ + std::map::const_iterator it = interconnect_physical_types_.find(pb_interconnect); + if (it == interconnect_physical_types_.end()) { + /* Return an invalid circuit model id */ + return NUM_INTERC_TYPES; + } + return interconnect_physical_types_.at(pb_interconnect); +} + /************************************************************************ * Public mutators ***********************************************************************/ @@ -157,4 +167,16 @@ void VprPbTypeAnnotation::add_interconnect_circuit_model(t_interconnect* pb_inte interconnect_circuit_models_[pb_interconnect] = circuit_model; } +void VprPbTypeAnnotation::add_interconnect_physical_type(t_interconnect* pb_interconnect, + const e_interconnect& physical_type) { + /* Warn any override attempt */ + std::map::const_iterator it = interconnect_physical_types_.find(pb_interconnect); + if (it != interconnect_physical_types_.end()) { + VTR_LOG_WARN("Override the physical interconnect for interconnect '%s'!\n", + pb_interconnect->name); + } + + interconnect_physical_types_[pb_interconnect] = physical_type; +} + } /* End namespace openfpga*/ diff --git a/openfpga/src/base/vpr_pb_type_annotation.h b/openfpga/src/base/vpr_pb_type_annotation.h index 7b19c3a29..824a81e87 100644 --- a/openfpga/src/base/vpr_pb_type_annotation.h +++ b/openfpga/src/base/vpr_pb_type_annotation.h @@ -36,6 +36,7 @@ class VprPbTypeAnnotation { BasicPort physical_pb_port_range(t_port* pb_port) const; CircuitModelId pb_type_circuit_model(t_pb_type* physical_pb_type) const; CircuitModelId interconnect_circuit_model(t_interconnect* pb_interconnect) const; + e_interconnect interconnect_physical_type(t_interconnect* pb_interconnect) 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); @@ -43,6 +44,7 @@ class VprPbTypeAnnotation { void add_physical_pb_port_range(t_port* operating_pb_port, const BasicPort& port_range); void add_pb_type_circuit_model(t_pb_type* physical_pb_type, const CircuitModelId& circuit_model); void add_interconnect_circuit_model(t_interconnect* pb_interconnect, const CircuitModelId& circuit_model); + void add_interconnect_physical_type(t_interconnect* pb_interconnect, const e_interconnect& physical_type); private: /* Internal data */ /* Pair a regular pb_type to its physical pb_type */ std::map physical_pb_types_; @@ -66,6 +68,12 @@ class VprPbTypeAnnotation { */ std::map interconnect_circuit_models_; + /* Physical type of interconnect + * Note: + * - only applicable to an interconnect belongs to physical mode + */ + std::map interconnect_physical_types_; + /* Pair a pb_type to its mode selection bits * - if the pb_type is a physical pb_type, the mode bits are the default mode * where the physical pb_type will operate when used