diff --git a/common/timing.cc b/common/timing.cc index 225afb5f..47c82a03 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -25,20 +25,72 @@ NEXTPNR_NAMESPACE_BEGIN +static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, + delay_t slack); + // Follow a path, returning budget to annotate -static delay_t follow_path(Context *ctx, const PortRef &begin, int path_length, - delay_t slack) +static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, + delay_t slack) { - if (ctx->getPortClock(begin.cell, begin.port) != IdString()) { - return slack / path_length; + delay_t value; + if (ctx->getPortClock(user.cell, user.port) != IdString()) { + // At the end of a timing path (arguably, should check setup time + // here too) + value = slack / path_length; } else { - // ... + // Default to the path ending here, if no further paths found + value = slack / path_length; + // Follow outputs of the user + for (auto port : user.cell->ports) { + if (port.second.type == PORT_OUT) { + delay_t comb_delay; + // Look up delay through this path + bool is_path = ctx->getCellDelay(user.cell, user.port, + port.first, comb_delay); + if (is_path) { + NetInfo *net = port.second.net; + if (net) { + delay_t path_budget = follow_net(ctx, net, path_length, + slack - comb_delay); + value = std::min(value, path_budget); + } + } + } + } } + + if (value < user.budget) { + user.budget = value; + } +} + +static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, + delay_t slack) +{ + delay_t net_budget = slack / path_length; + for (auto &usr : net->users) { + net_budget = std::min( + net_budget, follow_user_port(ctx, usr, path_length + 1, slack)); + } + return net_budget; } void assign_budget(Context *ctx, float default_clock) { - // TODO + for (auto cell : ctx->cells) { + for (auto port : cell.second->ports) { + if (port.second.type == PORT_OUT) { + IdString clock_domain = + ctx->getPortClock(cell.second, port.first); + if (clock_domain != IdString()) { + delay_t slack = delay_t( + 1.0e12 / default_clock); // TODO: clock constraints + if (port.second.net) + follow_net(ctx, port.second.net, 0, slack); + } + } + } + } } NEXTPNR_NAMESPACE_END diff --git a/dummy/arch.cc b/dummy/arch.cc index 64841374..fa0d8bd3 100644 --- a/dummy/arch.cc +++ b/dummy/arch.cc @@ -178,10 +178,10 @@ std::vector Arch::getPipGraphics(PipId pip) const // --------------------------------------------------------------- -delay_t Arch::getCellDelay(const CellInfo *cell, IdString fromPort, - IdString toPort) const +bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, + IdString toPort, delay_t &delay) const { - return 0; + return false; } IdString Arch::getPortClock(const CellInfo *cell, IdString port) const diff --git a/dummy/arch.h b/dummy/arch.h index 61a09e00..39c48232 100644 --- a/dummy/arch.h +++ b/dummy/arch.h @@ -134,8 +134,8 @@ struct Arch : BaseCtx std::unordered_set wireGraphicsReload; std::unordered_set pipGraphicsReload; - delay_t getCellDelay(const CellInfo *cell, IdString fromPort, - IdString toPort) const; + bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, + delay_t &delay) const; IdString getPortClock(const CellInfo *cell, IdString port) const; bool isClockPort(const CellInfo *cell, IdString port) const; }; diff --git a/ice40/arch.cc b/ice40/arch.cc index 380f3386..7baaef8d 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -418,11 +418,11 @@ std::vector Arch::getPipGraphics(PipId pip) const // ----------------------------------------------------------------------- -delay_t Arch::getCellDelay(const CellInfo *cell, IdString fromPort, - IdString toPort) const +bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, + IdString toPort, delay_t &delay) const { // TODO - return 0; + return false; } IdString Arch::getPortClock(const CellInfo *cell, IdString port) const diff --git a/ice40/arch.h b/ice40/arch.h index 172541c0..356f88da 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -758,9 +758,10 @@ struct Arch : BaseCtx // ------------------------------------------------- - // Get the delay through a cell from one port to another - delay_t getCellDelay(const CellInfo *cell, IdString fromPort, - IdString toPort) const; + // Get the delay through a cell from one port to another, returning false + // if no path exists + bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, + delay_t &delay) const; // Get the associated clock to a port, or empty if the port is combinational IdString getPortClock(const CellInfo *cell, IdString port) const; // Return true if a port is a clock