Merge branch 'refactoring' into dev
This commit is contained in:
commit
ee8f5542e9
|
@ -266,7 +266,7 @@
|
|||
<segment name="L4" circuit_model_name="chan_segment"/>
|
||||
</routing_segment>
|
||||
<direct_connection>
|
||||
<direct name="adder" circuit_model_name="direct_interc"/>
|
||||
<direct name="adder" circuit_model_name="direct_interc" type="column" x_dir="positive" y_dir="positive"/>
|
||||
</direct_connection>
|
||||
<pb_type_annotations>
|
||||
<!-- physical pb_type binding in complex block IO -->
|
||||
|
|
|
@ -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<e_direct_direction>(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] );
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
#ifndef ARCH_DIRECT_H
|
||||
#define ARCH_DIRECT_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#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<const char*, NUM_DIRECT_TYPES> 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<const char*, NUM_DIRECT_DIRECTIONS> 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<ArchDirectId, ArchDirectId>::const_iterator arch_direct_iterator;
|
||||
/* Create range */
|
||||
typedef vtr::Range<arch_direct_iterator> 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<ArchDirectId, ArchDirectId> direct_ids_;
|
||||
|
||||
/* Unique name: the identifier to annotate the original direct connection in VPR architecture */
|
||||
vtr::vector<ArchDirectId, std::string> names_;
|
||||
|
||||
/* circuit model: the circuit model to used to implement this connection */
|
||||
vtr::vector<ArchDirectId, CircuitModelId> circuit_models_;
|
||||
|
||||
/* type: if this connection should be cross-column or cross-row */
|
||||
vtr::vector<ArchDirectId, e_direct_type> 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<ArchDirectId, vtr::Point<e_direct_direction>> directions_;
|
||||
|
||||
/* Fast look-up */
|
||||
std::map<std::string, ArchDirectId> direct_name2ids_;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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<arch_direct_id_tag> ArchDirectId;
|
||||
|
||||
/* Short declaration of class */
|
||||
class ArchDirect;
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -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<std::string, CircuitModelId> direct2circuit;
|
||||
ArchDirect arch_direct;
|
||||
|
||||
/* Pb type annotations
|
||||
* Bind from operating to physical
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -185,20 +185,53 @@ std::map<std::string, CircuitModelId> 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 <direct_connection> to an object of name-to-circuit mapping
|
||||
* Note: this function should be called AFTER the parsing of circuit library!!!
|
||||
*******************************************************************/
|
||||
std::map<std::string, CircuitModelId> read_xml_direct_circuit(pugi::xml_node& Node,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
const CircuitLibrary& circuit_lib) {
|
||||
std::map<std::string, CircuitModelId> 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<std::string, CircuitModelId> 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<std::string, CircuitModelId>::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;
|
||||
}
|
||||
|
|
|
@ -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<std::string, CircuitModelId> read_xml_routing_segment_circuit(pugi::xml
|
|||
const pugiutil::loc_data& loc_data,
|
||||
const CircuitLibrary& circuit_lib);
|
||||
|
||||
std::map<std::string, CircuitModelId> 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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<std::string, CircuitModelId>& 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" << "<direct_connection>" << "\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" << "</direct_connection>" << "\n";
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <string>
|
||||
|
||||
#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<std::string, CircuitModelId>& direct2circuit);
|
||||
const ArchDirect& arch_direct);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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<size_t, CircuitModelId>::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<size_t, CircuitModelId>::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*/
|
||||
|
|
|
@ -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<t_pb_type*, t_pb_type*> physical_pb_types_;
|
||||
|
@ -173,8 +174,8 @@ class VprDeviceAnnotation {
|
|||
/* Pair a Routing Segment (rr_segment) to a circuit model */
|
||||
std::map<RRSegmentId, CircuitModelId> rr_segment_circuit_models_;
|
||||
|
||||
/* Pair a direct connection (direct) to a circuit model */
|
||||
std::map<size_t, CircuitModelId> direct_circuit_models_;
|
||||
/* Pair a direct connection (direct) to a annotation which contains circuit model id */
|
||||
std::map<size_t, ArchDirectId> direct_annotations_;
|
||||
};
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
@ -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<const OpenfpgaContext&>(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 */
|
||||
|
|
|
@ -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<size_t>& grid_instance_ids,
|
||||
const RRGraph& rr_graph,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const RRGSB& rr_gsb,
|
||||
const vtr::Matrix<size_t>& 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<size_t> instance_sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||
vtr::Point<size_t> 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<size_t> 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<size_t> 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<size_t> 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<size_t> 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<size_t>& grid_instance_ids,
|
||||
const RRGraph& rr_graph,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const RRGSB& rr_gsb,
|
||||
const vtr::Matrix<size_t>& 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<size_t> instance_sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||
vtr::Point<size_t> 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<size_t> 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<size_t> 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<e_side, bool> 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<size_t> 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<size_t> 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<size_t>& 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<size_t>& 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<size_t> instance_cb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
vtr::Point<size_t> 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<size_t> 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<size_t> 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<enum e_side> 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<size_t> 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<size_t> 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<size_t>& sb_instance_ids,
|
||||
const std::map<t_rr_type, vtr::Matrix<size_t>>& 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<size_t> instance_sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||
vtr::Point<size_t> 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<size_t> 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<size_t> 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<size_t> instance_gsb_cb_coordinate = find_top_module_gsb_coordinate_by_sb_side(rr_gsb, side_manager.get_side());
|
||||
vtr::Point<size_t> 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<size_t> 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<size_t> 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<size_t>& grid_instance_ids,
|
||||
const RRGraph& rr_graph,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const vtr::Matrix<size_t>& sb_instance_ids,
|
||||
const std::map<t_rr_type, vtr::Matrix<size_t>>& cb_instance_ids,
|
||||
const bool& compact_routing_hierarchy,
|
||||
const bool& duplicate_grid_pin) {
|
||||
|
||||
vtr::Point<size_t> 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<size_t> 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 */
|
|
@ -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 <vector>
|
||||
#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<size_t>& grid_instance_ids,
|
||||
const RRGraph& rr_graph,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const vtr::Matrix<size_t>& sb_instance_ids,
|
||||
const std::map<t_rr_type, vtr::Matrix<size_t>>& cb_instance_ids,
|
||||
const bool& compact_routing_hierarchy,
|
||||
const bool& duplicate_grid_pin);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -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<size_t>& 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<size_t> 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<size_t>(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<size_t>& grid_instance_ids,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const vtr::Matrix<size_t>& sb_instance_ids,
|
||||
const std::map<t_rr_type, vtr::Matrix<size_t>>& cb_instance_ids,
|
||||
const bool& compact_routing_hierarchy,
|
||||
const vtr::Point<size_t>& tile_coord,
|
||||
const e_side& tile_border_side) {
|
||||
|
||||
vtr::Point<size_t> gsb_coord_range = device_rr_gsb.get_gsb_range();
|
||||
|
||||
vtr::Point<size_t> 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<size_t> 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<size_t>& grid_instance_ids,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const vtr::Matrix<size_t>& sb_instance_ids,
|
||||
const std::map<t_rr_type, vtr::Matrix<size_t>>& 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<e_side> io_sides{BOTTOM, RIGHT, TOP, LEFT};
|
||||
std::map<e_side, std::vector<vtr::Point<size_t>>> io_coords;
|
||||
|
||||
/* BOTTOM side I/Os */
|
||||
for (size_t ix = 1; ix < grids.width() - 1; ++ix) {
|
||||
io_coords[BOTTOM].push_back(vtr::Point<size_t>(ix, 0));
|
||||
}
|
||||
|
||||
/* RIGHT side I/Os */
|
||||
for (size_t iy = 1; iy < grids.height() - 1; ++iy) {
|
||||
io_coords[RIGHT].push_back(vtr::Point<size_t>(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<size_t>(ix, grids.height() - 1));
|
||||
}
|
||||
io_coords[TOP].push_back(vtr::Point<size_t>(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<size_t>(0, iy));
|
||||
}
|
||||
|
||||
for (const e_side& io_side : io_sides) {
|
||||
for (const vtr::Point<size_t>& 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<vtr::Point<size_t>> 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<size_t>(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<size_t>(ix, iy));
|
||||
}
|
||||
}
|
||||
/* Flip the positive direction to be negative */
|
||||
positive_direction = !positive_direction;
|
||||
}
|
||||
|
||||
for (const vtr::Point<size_t>& 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 */
|
|
@ -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 <vector>
|
||||
#include <map>
|
||||
#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<size_t>& grid_instance_ids,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const vtr::Matrix<size_t>& sb_instance_ids,
|
||||
const std::map<t_rr_type, vtr::Matrix<size_t>>& 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
|
|
@ -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<size_t>& grid_coord) {
|
||||
/* Determine if the grid locates at the border */
|
||||
vtr::Point<size_t> 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<size_t> to replace DeviceCoordinator
|
||||
*******************************************************************/
|
||||
vtr::Point<size_t> 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<size_t> 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 */
|
|
@ -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 <vector>
|
||||
#include <string>
|
||||
#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<size_t>& grid_coord);
|
||||
|
||||
t_rr_type find_top_module_cb_type_by_sb_side(const e_side& sb_side);
|
||||
|
||||
vtr::Point<size_t> find_top_module_gsb_coordinate_by_sb_side(const RRGSB& rr_gsb,
|
||||
const e_side& sb_side);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -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:
|
||||
* <tile_type_name>.<pin_name>[<pin_lsb>:<pin_msb>]
|
||||
***************************************************************************************/
|
||||
static
|
||||
std::string parse_direct_tile_name(const std::string& direct_tile_inf) {
|
||||
StringToken tokenizer(direct_tile_inf);
|
||||
std::vector<std::string> 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 <tile_type_name>.<pin_name>[<pin_lsb>:<pin_msb>].\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<size_t> 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<size_t> 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<size_t> 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<size_t> from_grid_coord(x, y);
|
||||
vtr::Point<size_t> 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<size_t> 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<size_t> 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<size_t> from_grid_coord(x, y);
|
||||
vtr::Point<size_t> 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<size_t> 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 */
|
|
@ -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
|
|
@ -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<size_t> 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<size_t> 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<size_t>& 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<size_t>& 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 */
|
|
@ -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<TileDirectId, TileDirectId>::const_iterator tile_direct_iterator;
|
||||
typedef vtr::Range<tile_direct_iterator> 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<size_t> 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<size_t> 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<size_t>& 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<size_t>& 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<TileDirectId, TileDirectId> direct_ids_;
|
||||
|
||||
/* Detailed information about the starting tile
|
||||
* - tile type description
|
||||
* - tile coordinate
|
||||
* - tile pin id
|
||||
*/
|
||||
vtr::vector<TileDirectId, t_physical_tile_type_ptr> from_tiles_;
|
||||
vtr::vector<TileDirectId, vtr::Point<size_t>> from_tile_coords_;
|
||||
vtr::vector<TileDirectId, e_side> from_tile_sides_;
|
||||
vtr::vector<TileDirectId, size_t> from_tile_pins_;
|
||||
|
||||
/* Detailed information about the ending tile
|
||||
* - tile type description
|
||||
* - tile coordinate
|
||||
* - tile pin id
|
||||
*/
|
||||
vtr::vector<TileDirectId, t_physical_tile_type_ptr> to_tiles_;
|
||||
vtr::vector<TileDirectId, vtr::Point<size_t>> to_tile_coords_;
|
||||
vtr::vector<TileDirectId, e_side> to_tile_sides_;
|
||||
vtr::vector<TileDirectId, size_t> to_tile_pins_;
|
||||
|
||||
vtr::vector<TileDirectId, ArchDirectId> arch_directs_;
|
||||
};
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
|
@ -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<tile_direct_id_tag> TileDirectId;
|
||||
|
||||
class TileDirect;
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -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"
|
||||
|
|
|
@ -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<enum e_side> 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 */
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef RR_GSB_UTILS_H
|
||||
#define RR_GSB_UTILS_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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
|
Loading…
Reference in New Issue