Skip to content

FPGA interchange: add RR graph generation #1999

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 6 additions & 11 deletions libs/EXTERNAL/libinterchange/interchange/DeviceResources.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,8 @@ annotation wireRef(*) :WireRef;
using WireIdx = UInt32;

struct WireTypeRef {
type @0 :Ref.ReferenceType = parent;
type @0 :Ref.ReferenceType = rootValue;
field @1 :Text = "wireTypes";
depth @2 :Int32 = 1;
}
annotation wireTypeRef(*) :WireTypeRef;
using WireTypeIdx = UInt32;
Expand All @@ -81,19 +80,15 @@ using TileTypeSiteTypeIdx = UInt32;
using TileTypeSubTileIdx = UInt16;

struct PIPTimingRef {
type @0 :Ref.ReferenceType = parent;
field @1 :Text = "pipTimingList";
depth @2 :Int32 = 1;

type @0 :Ref.ReferenceType = rootValue;
field @1 :Text = "pipTimings";
}
annotation pipTimingRef(*) :PIPTimingRef;
using PipTimingIdx = UInt32;

struct NodeTimingRef {
type @0 :Ref.ReferenceType = parent;
field @1 :Text = "nodeTimingList";
depth @2 :Int32 = 1;

type @0 :Ref.ReferenceType = rootValue;
field @1 :Text = "nodeTimings";
}
annotation nodeTimingRef(*) :NodeTimingRef;
using NodeTimingIdx = UInt32;
Expand Down Expand Up @@ -632,7 +627,7 @@ struct Device {
}

struct PinDelay {
pin @0 : BELPinIdx $belPinRef();
pin @0 : BELPinIdx;
union {
noClock @1 : Void;
clockEdge @2 : ClockEdge;
Expand Down
185 changes: 185 additions & 0 deletions libs/libarchfpga/src/fpga_interchange_arch_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#include "fpga_interchange_arch_utils.h"

/****************** Utility functions ******************/

/**
* @brief The FPGA interchange timing model includes three different corners (min, typ and max) for each of the two
* speed_models (slow and fast).
*
* Timing data can be found on PIPs, nodes, site pins and bel pins.
* This function retrieves the timing value based on the wanted speed model and the wanted corner.
*
* Corner model is considered valid if at least one configuration is set.
* In that case this value shall be returned.
*
* More information on the FPGA Interchange timing model can be found here:
* - https://github.com/chipsalliance/fpga-interchange-schema/blob/main/interchange/DeviceResources.capnp
*/

float get_corner_value(DeviceResources::Device::CornerModel::Reader model, const char* speed_model, const char* value) {
bool slow_model = std::string(speed_model) == std::string("slow");
bool fast_model = std::string(speed_model) == std::string("fast");

bool min_corner = std::string(value) == std::string("min");
bool typ_corner = std::string(value) == std::string("typ");
bool max_corner = std::string(value) == std::string("max");

if (!slow_model && !fast_model) {
archfpga_throw("", __LINE__, "Wrong speed model `%s`. Expected `slow` or `fast`\n", speed_model);
}

if (!min_corner && !typ_corner && !max_corner) {
archfpga_throw("", __LINE__, "Wrong corner model `%s`. Expected `min`, `typ` or `max`\n", value);
}

bool has_fast = model.getFast().hasFast();
bool has_slow = model.getSlow().hasSlow();

if ((slow_model || (fast_model && !has_fast)) && has_slow) {
auto half = model.getSlow().getSlow();
if (min_corner && half.getMin().isMin()) {
return half.getMin().getMin();
} else if (typ_corner && half.getTyp().isTyp()) {
return half.getTyp().getTyp();
} else if (max_corner && half.getMax().isMax()) {
return half.getMax().getMax();
} else {
if (half.getMin().isMin()) {
return half.getMin().getMin();
} else if (half.getTyp().isTyp()) {
return half.getTyp().getTyp();
} else if (half.getMax().isMax()) {
return half.getMax().getMax();
} else {
archfpga_throw("", __LINE__, "Invalid speed model %s. No value found!\n", speed_model);
}
}
} else if ((fast_model || slow_model) && has_fast) {
auto half = model.getFast().getFast();
if (min_corner && half.getMin().isMin()) {
return half.getMin().getMin();
} else if (typ_corner && half.getTyp().isTyp()) {
return half.getTyp().getTyp();
} else if (max_corner && half.getMax().isMax()) {
return half.getMax().getMax();
} else {
if (half.getMin().isMin()) {
return half.getMin().getMin();
} else if (half.getTyp().isTyp()) {
return half.getTyp().getTyp();
} else if (half.getMax().isMax()) {
return half.getMax().getMax();
} else {
archfpga_throw("", __LINE__, "Invalid speed model %s. No value found!\n", speed_model);
}
}
}
return 0.;
}

char* int_to_string(char* buff, int value) {
if (value < 10) {
return &(*buff = '0' + value) + 1;
} else {
return &(*int_to_string(buff, value / 10) = '0' + value % 10) + 1;
}
}

void pip_types(std::set<std::tuple<int, bool>>& seen,
DeviceResources::Device::Reader ar_) {
for (const auto& tile : ar_.getTileTypeList()) {
for (const auto& pip : tile.getPips()) {
/*
* FIXME
* right now we allow for pseudo pips even tho we don't check if they are avaiable
* if (pip.isPseudoCells())
* continue;
*/
seen.emplace(pip.getTiming(), pip.getBuffered21());
if (!pip.getDirectional()) {
seen.emplace(pip.getTiming(), pip.getBuffered20());
}
}
}
}

void fill_switch(t_arch_switch_inf* switch_,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you use template here to be compatible with t_switch_inf and t_rr_switch_inf. Maybe it is better to note in code comments for developers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add comment that process_switches_array() is template function.

float R,
float Cin,
float Cout,
float Cinternal,
float Tdel,
float mux_trans_size,
float buf_size,
char* name,
SwitchType type) {
switch_->R = R;
switch_->Cin = Cin;
switch_->Cout = Cout;
switch_->Cinternal = Cinternal;
switch_->set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, Tdel);
switch_->mux_trans_size = mux_trans_size;
switch_->buf_size = buf_size;
switch_->name = name;
switch_->set_type(type);
}

void process_cell_bel_mappings(DeviceResources::Device::Reader ar_,
std::unordered_map<uint32_t, std::set<t_bel_cell_mapping>>& bel_cell_mappings_,
std::function<std::string(size_t)> str) {
auto primLib = ar_.getPrimLibs();
auto portList = primLib.getPortList();

for (auto cell_mapping : ar_.getCellBelMap()) {
size_t cell_name = cell_mapping.getCell();

int found_valid_prim = false;
for (auto primitive : primLib.getCellDecls()) {
if (cell_name != primitive.getName()) continue;
if (str(primitive.getLib()) != std::string("primitives")) continue;

bool has_inout = false;
for (auto port_idx : primitive.getPorts()) {
auto port = portList[port_idx];

if (port.getDir() == INOUT) {
has_inout = true;
break;
}
}

if (!has_inout) {
found_valid_prim = true;
break;
}
}

if (!found_valid_prim)
continue;

for (auto common_pins : cell_mapping.getCommonPins()) {
std::vector<std::pair<size_t, size_t>> pins;

for (auto pin_map : common_pins.getPins())
pins.emplace_back(pin_map.getCellPin(), pin_map.getBelPin());

for (auto site_type_entry : common_pins.getSiteTypes()) {
size_t site_type = site_type_entry.getSiteType();

for (auto bel : site_type_entry.getBels()) {
t_bel_cell_mapping mapping;

mapping.cell = cell_name;
mapping.site = site_type;
mapping.pins = pins;

std::set<t_bel_cell_mapping> maps{mapping};
auto res = bel_cell_mappings_.emplace(bel, maps);
if (!res.second) {
res.first->second.insert(mapping);
}
}
}
}
}
}
98 changes: 98 additions & 0 deletions libs/libarchfpga/src/fpga_interchange_arch_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#ifndef FPGAINTERCHANGE_ARCH_UTILS_FILE_H
#define FPGAINTERCHANGE_ARCH_UTILS_FILE_H

#include "arch_types.h"
#include "arch_util.h"
#include "arch_error.h"
#include "vtr_error.h"
#include "vtr_util.h"

#include "DeviceResources.capnp.h"
#include "LogicalNetlist.capnp.h"
#include "capnp/serialize.h"
#include "capnp/serialize-packed.h"
#include <fcntl.h>
#include <unistd.h>
#include <set>
#include <string>

#ifdef __cplusplus
extern "C" {
#endif

// Necessary to reduce code verbosity when getting the pin directions
static const auto INPUT = LogicalNetlist::Netlist::Direction::INPUT;
static const auto OUTPUT = LogicalNetlist::Netlist::Direction::OUTPUT;
static const auto INOUT = LogicalNetlist::Netlist::Direction::INOUT;

struct t_bel_cell_mapping {
size_t cell;
size_t site;
std::vector<std::pair<size_t, size_t>> pins;

bool operator<(const t_bel_cell_mapping& other) const {
return cell < other.cell || (cell == other.cell && site < other.site);
}
};

/****************** Utility functions ******************/

/**
* @brief The FPGA interchange timing model includes three different corners (min, typ and max) for each of the two
* speed_models (slow and fast).
*
* Timing data can be found on PIPs, nodes, site pins and bel pins.
* This function retrieves the timing value based on the wanted speed model and the wanted corner.
*
* Corner model is considered valid if at least one configuration is set.
* In that case this value shall be returned.
*
* More information on the FPGA Interchange timing model can be found here:
* - https://github.com/chipsalliance/fpga-interchange-schema/blob/main/interchange/DeviceResources.capnp
*/

float get_corner_value(DeviceResources::Device::CornerModel::Reader model, const char* speed_model, const char* value);

char* int_to_string(char* buff, int value);

void pip_types(std::set<std::tuple<int, bool>>& seen,
DeviceResources::Device::Reader ar_);

void process_cell_bel_mappings(DeviceResources::Device::Reader ar_,
std::unordered_map<uint32_t, std::set<t_bel_cell_mapping>>& bel_cell_mappings_,
std::function<std::string(size_t)> str);

#ifdef __cplusplus
}
#endif
template<typename T>
void fill_switch(T* switch_,
float R,
float Cin,
float Cout,
float Cinternal,
float Tdel,
float mux_trans_size,
float buf_size,
char* name,
SwitchType type);

void fill_switch(t_arch_switch_inf* switch_,
float R,
float Cin,
float Cout,
float Cinternal,
float Tdel,
float mux_trans_size,
float buf_size,
char* name,
SwitchType type);

template<typename T1, typename T2>
void process_switches_array(DeviceResources::Device::Reader ar_,
std::set<std::tuple<int, bool>>& seen,
T1 switch_array,
std::vector<std::tuple<std::tuple<int, bool>, int>>& pips_models_);

#include "fpga_interchange_arch_utils.impl.h"
#endif
Loading