timing: Import cell delays to our own structures
Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
parent
fac6a6c068
commit
d0772ce1e3
@ -30,6 +30,100 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
void TimingAnalyser::setup()
|
||||||
|
{
|
||||||
|
init_ports();
|
||||||
|
get_cell_delays();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimingAnalyser::init_ports()
|
||||||
|
{
|
||||||
|
// Per cell port structures
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
for (auto port : sorted_ref(ci->ports)) {
|
||||||
|
auto &data = ports[CellPortKey(ci->name, port.first)];
|
||||||
|
data.cell_port = CellPortKey(ci->name, port.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Cell port to net port mapping
|
||||||
|
for (auto net : sorted(ctx->nets)) {
|
||||||
|
NetInfo *ni = net.second;
|
||||||
|
if (ni->driver.cell != nullptr)
|
||||||
|
ports[CellPortKey(ni->driver)].net_port = NetPortKey(ni->name);
|
||||||
|
for (size_t i = 0; i < ni->users.size(); i++)
|
||||||
|
ports[CellPortKey(ni->users.at(i))].net_port = NetPortKey(ni->name, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimingAnalyser::get_cell_delays()
|
||||||
|
{
|
||||||
|
for (auto &port : ports) {
|
||||||
|
CellInfo *ci = cell_info(port.first);
|
||||||
|
auto &pi = port_info(port.first);
|
||||||
|
auto &pd = port.second;
|
||||||
|
|
||||||
|
IdString name = port.first.port;
|
||||||
|
// Ignore dangling ports altogether for timing purposes
|
||||||
|
if (pd.net_port.net == IdString())
|
||||||
|
continue;
|
||||||
|
pd.cell_arcs.clear();
|
||||||
|
int clkInfoCount = 0;
|
||||||
|
TimingPortClass cls = ctx->getPortTimingClass(ci, name, clkInfoCount);
|
||||||
|
if (cls == TMG_STARTPOINT || cls == TMG_ENDPOINT || cls == TMG_CLOCK_INPUT || cls == TMG_GEN_CLOCK ||
|
||||||
|
cls == TMG_IGNORE)
|
||||||
|
continue;
|
||||||
|
if (pi.type == PORT_IN) {
|
||||||
|
// Input ports might have setup/hold relationships
|
||||||
|
if (cls == TMG_REGISTER_INPUT) {
|
||||||
|
for (int i = 0; i < clkInfoCount; i++) {
|
||||||
|
auto info = ctx->getPortClockingInfo(ci, name, i);
|
||||||
|
pd.cell_arcs.emplace_back(CellArc::SETUP, info.clock_port, DelayQuad(info.setup, info.setup),
|
||||||
|
info.edge);
|
||||||
|
pd.cell_arcs.emplace_back(CellArc::HOLD, info.clock_port, DelayQuad(info.hold, info.hold),
|
||||||
|
info.edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Combinational delays through cell
|
||||||
|
for (auto &other_port : ci->ports) {
|
||||||
|
auto &op = other_port.second;
|
||||||
|
// ignore dangling ports and non-outputs
|
||||||
|
if (op.net == nullptr || op.type != PORT_OUT)
|
||||||
|
continue;
|
||||||
|
DelayQuad delay;
|
||||||
|
bool is_path = ctx->getCellDelay(ci, name, other_port.first, delay);
|
||||||
|
if (is_path)
|
||||||
|
pd.cell_arcs.emplace_back(CellArc::COMBINATIONAL, other_port.first, delay);
|
||||||
|
}
|
||||||
|
} else if (pi.type == PORT_OUT) {
|
||||||
|
// Output ports might have clk-to-q relationships
|
||||||
|
if (cls == TMG_REGISTER_OUTPUT) {
|
||||||
|
for (int i = 0; i < clkInfoCount; i++) {
|
||||||
|
auto info = ctx->getPortClockingInfo(ci, name, i);
|
||||||
|
pd.cell_arcs.emplace_back(CellArc::CLK_TO_Q, info.clock_port, info.clockToQ, info.edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Combinational delays through cell
|
||||||
|
for (auto &other_port : ci->ports) {
|
||||||
|
auto &op = other_port.second;
|
||||||
|
// ignore dangling ports and non-inputs
|
||||||
|
if (op.net == nullptr || op.type != PORT_IN)
|
||||||
|
continue;
|
||||||
|
DelayQuad delay;
|
||||||
|
bool is_path = ctx->getCellDelay(ci, other_port.first, name, delay);
|
||||||
|
if (is_path)
|
||||||
|
pd.cell_arcs.emplace_back(CellArc::COMBINATIONAL, other_port.first, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CellInfo *TimingAnalyser::cell_info(const CellPortKey &key) { return ctx->cells.at(key.cell).get(); }
|
||||||
|
|
||||||
|
PortInfo &TimingAnalyser::port_info(const CellPortKey &key) { return ctx->cells.at(key.cell)->ports.at(key.port); }
|
||||||
|
|
||||||
|
/** LEGACY CODE BEGIN **/
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct ClockEvent
|
struct ClockEvent
|
||||||
{
|
{
|
||||||
@ -1005,6 +1099,10 @@ void get_criticalities(Context *ctx, NetCriticalityMap *net_crit)
|
|||||||
net_crit->clear();
|
net_crit->clear();
|
||||||
Timing timing(ctx, true, true, &crit_paths, nullptr, net_crit);
|
Timing timing(ctx, true, true, &crit_paths, nullptr, net_crit);
|
||||||
timing.walk_paths();
|
timing.walk_paths();
|
||||||
|
|
||||||
|
// Test the new timing analyser, too
|
||||||
|
TimingAnalyser sta_v2(ctx);
|
||||||
|
sta_v2.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -26,6 +26,14 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
struct CellPortKey
|
struct CellPortKey
|
||||||
{
|
{
|
||||||
|
CellPortKey(){};
|
||||||
|
CellPortKey(IdString cell, IdString port) : cell(cell), port(port){};
|
||||||
|
explicit CellPortKey(const PortRef &pr)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(pr.cell != nullptr);
|
||||||
|
cell = pr.cell->name;
|
||||||
|
port = pr.port;
|
||||||
|
}
|
||||||
IdString cell, port;
|
IdString cell, port;
|
||||||
struct Hash
|
struct Hash
|
||||||
{
|
{
|
||||||
@ -43,6 +51,10 @@ struct NetPortKey
|
|||||||
{
|
{
|
||||||
IdString net;
|
IdString net;
|
||||||
size_t idx;
|
size_t idx;
|
||||||
|
NetPortKey(){};
|
||||||
|
explicit NetPortKey(IdString net) : net(net), idx(DRIVER_IDX){}; // driver
|
||||||
|
explicit NetPortKey(IdString net, size_t user) : net(net), idx(user){}; // user
|
||||||
|
|
||||||
static const size_t DRIVER_IDX = std::numeric_limits<size_t>::max();
|
static const size_t DRIVER_IDX = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
inline bool is_driver() const { return (idx == DRIVER_IDX); }
|
inline bool is_driver() const { return (idx == DRIVER_IDX); }
|
||||||
@ -87,8 +99,11 @@ struct TimingAnalyser
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TimingAnalyser(Context *ctx) : ctx(ctx){};
|
TimingAnalyser(Context *ctx) : ctx(ctx){};
|
||||||
|
void setup();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void init_ports();
|
||||||
|
void get_cell_delays();
|
||||||
// To avoid storing the domain tag structure (which could get large when considering more complex constrained tag
|
// To avoid storing the domain tag structure (which could get large when considering more complex constrained tag
|
||||||
// cases), assign each domain an ID and use that instead
|
// cases), assign each domain an ID and use that instead
|
||||||
typedef int domain_id_t;
|
typedef int domain_id_t;
|
||||||
@ -113,6 +128,7 @@ struct TimingAnalyser
|
|||||||
// A cell timing arc, used to cache cell timings and reduce the number of potentially-expensive Arch API calls
|
// A cell timing arc, used to cache cell timings and reduce the number of potentially-expensive Arch API calls
|
||||||
struct CellArc
|
struct CellArc
|
||||||
{
|
{
|
||||||
|
|
||||||
enum ArcType
|
enum ArcType
|
||||||
{
|
{
|
||||||
COMBINATIONAL,
|
COMBINATIONAL,
|
||||||
@ -120,10 +136,16 @@ struct TimingAnalyser
|
|||||||
HOLD,
|
HOLD,
|
||||||
CLK_TO_Q
|
CLK_TO_Q
|
||||||
} type;
|
} type;
|
||||||
|
|
||||||
IdString other_port;
|
IdString other_port;
|
||||||
DelayQuad value;
|
DelayQuad value;
|
||||||
// Clock polarity, not used for combinational arcs
|
// Clock polarity, not used for combinational arcs
|
||||||
ClockEdge edge;
|
ClockEdge edge;
|
||||||
|
|
||||||
|
CellArc(ArcType type, IdString other_port, DelayQuad value)
|
||||||
|
: type(type), other_port(other_port), value(value), edge(RISING_EDGE){};
|
||||||
|
CellArc(ArcType type, IdString other_port, DelayQuad value, ClockEdge edge)
|
||||||
|
: type(type), other_port(other_port), value(value), edge(edge){};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Timing data for every cell port
|
// Timing data for every cell port
|
||||||
@ -139,6 +161,9 @@ struct TimingAnalyser
|
|||||||
DelayPair route_delay;
|
DelayPair route_delay;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CellInfo *cell_info(const CellPortKey &key);
|
||||||
|
PortInfo &port_info(const CellPortKey &key);
|
||||||
|
|
||||||
std::unordered_map<CellPortKey, PerPort, CellPortKey::Hash> ports;
|
std::unordered_map<CellPortKey, PerPort, CellPortKey::Hash> ports;
|
||||||
std::unordered_map<ClockDomainKey, domain_id_t, ClockDomainKey::Hash> domain_to_id;
|
std::unordered_map<ClockDomainKey, domain_id_t, ClockDomainKey::Hash> domain_to_id;
|
||||||
std::vector<ClockDomainKey> id_to_domain;
|
std::vector<ClockDomainKey> id_to_domain;
|
||||||
|
Loading…
Reference in New Issue
Block a user