Working on adding multiple domains to timing analysis
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
b6312abc5d
commit
3ca02cc55c
120
common/timing.cc
120
common/timing.cc
@ -29,6 +29,46 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct ClockEvent {
|
||||||
|
IdString clock;
|
||||||
|
enum {
|
||||||
|
POSEDGE,
|
||||||
|
NEGEDGE
|
||||||
|
} edge;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ClockPair {
|
||||||
|
ClockEvent start, end;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct hash<NEXTPNR_NAMESPACE_PREFIX ClockEvent> {
|
||||||
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX ClockEvent &obj) const noexcept {
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(obj.clock));
|
||||||
|
boost::hash_combine(seed, hash<int>()(int(obj.edge)));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct hash<NEXTPNR_NAMESPACE_PREFIX ClockPair> {
|
||||||
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX ClockPair &obj) const noexcept {
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX ClockEvent>()(obj.start));
|
||||||
|
boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX ClockEvent>()(obj.start));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
typedef std::vector<const PortRef *> PortRefVector;
|
typedef std::vector<const PortRef *> PortRefVector;
|
||||||
typedef std::map<int, unsigned> DelayFrequency;
|
typedef std::map<int, unsigned> DelayFrequency;
|
||||||
|
|
||||||
@ -41,11 +81,20 @@ struct Timing
|
|||||||
PortRefVector *crit_path;
|
PortRefVector *crit_path;
|
||||||
DelayFrequency *slack_histogram;
|
DelayFrequency *slack_histogram;
|
||||||
|
|
||||||
|
struct ClockDomain
|
||||||
|
{
|
||||||
|
IdString net;
|
||||||
|
enum {
|
||||||
|
RISING,
|
||||||
|
FALLING
|
||||||
|
} edge;
|
||||||
|
};
|
||||||
|
|
||||||
struct TimingData
|
struct TimingData
|
||||||
{
|
{
|
||||||
TimingData() : max_arrival(), max_path_length(), min_remaining_budget() {}
|
TimingData() : max_arrival(), max_path_length(), min_remaining_budget() {}
|
||||||
TimingData(delay_t max_arrival) : max_arrival(max_arrival), max_path_length(), min_remaining_budget() {}
|
TimingData(ClockPair dest, delay_t max_arrival) : max_path_length(), min_remaining_budget() {}
|
||||||
delay_t max_arrival;
|
std::unordedelay_t max_arrival;
|
||||||
unsigned max_path_length = 0;
|
unsigned max_path_length = 0;
|
||||||
delay_t min_remaining_budget;
|
delay_t min_remaining_budget;
|
||||||
bool false_startpoint = false;
|
bool false_startpoint = false;
|
||||||
@ -65,7 +114,7 @@ struct Timing
|
|||||||
// First, compute the topographical order of nets to walk through the circuit, assuming it is a _acyclic_ graph
|
// First, compute the topographical order of nets to walk through the circuit, assuming it is a _acyclic_ graph
|
||||||
// TODO(eddieh): Handle the case where it is cyclic, e.g. combinatorial loops
|
// TODO(eddieh): Handle the case where it is cyclic, e.g. combinatorial loops
|
||||||
std::vector<NetInfo *> topographical_order;
|
std::vector<NetInfo *> topographical_order;
|
||||||
std::unordered_map<const NetInfo *, TimingData> net_data;
|
std::unordered_map<const NetInfo *, std::unordered_map<ClockEvent, TimingData>> net_data;
|
||||||
// In lieu of deleting edges from the graph, simply count the number of fanins to each output port
|
// In lieu of deleting edges from the graph, simply count the number of fanins to each output port
|
||||||
std::unordered_map<const PortInfo *, unsigned> port_fanin;
|
std::unordered_map<const PortInfo *, unsigned> port_fanin;
|
||||||
|
|
||||||
@ -92,13 +141,13 @@ struct Timing
|
|||||||
DelayInfo clkToQ;
|
DelayInfo clkToQ;
|
||||||
ctx->getCellDelay(cell.second.get(), clockPort, o->name, clkToQ);
|
ctx->getCellDelay(cell.second.get(), clockPort, o->name, clkToQ);
|
||||||
topographical_order.emplace_back(o->net);
|
topographical_order.emplace_back(o->net);
|
||||||
net_data.emplace(o->net, TimingData{clkToQ.maxDelay()});
|
net_data[o->net][ClockEvent{IdString(), ClockEvent::POSEDGE}] = TimingData{clkToQ.maxDelay()};
|
||||||
} else {
|
} else {
|
||||||
if (portClass == TMG_STARTPOINT || portClass == TMG_GEN_CLOCK || portClass == TMG_IGNORE) {
|
if (portClass == TMG_STARTPOINT || portClass == TMG_GEN_CLOCK || portClass == TMG_IGNORE) {
|
||||||
topographical_order.emplace_back(o->net);
|
topographical_order.emplace_back(o->net);
|
||||||
TimingData td;
|
TimingData td;
|
||||||
td.false_startpoint = (portClass == TMG_GEN_CLOCK || portClass == TMG_IGNORE);
|
td.false_startpoint = (portClass == TMG_GEN_CLOCK || portClass == TMG_IGNORE);
|
||||||
net_data.emplace(o->net, std::move(td));
|
net_data[o->net][ClockEvent{IdString(), ClockEvent::POSEDGE}] = td;
|
||||||
}
|
}
|
||||||
// Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and
|
// Otherwise, for all driven input ports on this cell, if a timing arc exists between the input and
|
||||||
// the current output port, increment fanin counter
|
// the current output port, increment fanin counter
|
||||||
@ -174,38 +223,43 @@ struct Timing
|
|||||||
|
|
||||||
// Go forwards topographically to find the maximum arrival time and max path length for each net
|
// Go forwards topographically to find the maximum arrival time and max path length for each net
|
||||||
for (auto net : topographical_order) {
|
for (auto net : topographical_order) {
|
||||||
auto &nd = net_data.at(net);
|
auto &nd_map = net_data.at(net);
|
||||||
const auto net_arrival = nd.max_arrival;
|
for (auto &startdomain : nd_map) {
|
||||||
const auto net_length_plus_one = nd.max_path_length + 1;
|
ClockEvent start_clk = startdomain.first;
|
||||||
nd.min_remaining_budget = clk_period;
|
auto &nd = startdomain.second;
|
||||||
for (auto &usr : net->users) {
|
const auto net_arrival = nd.max_arrival;
|
||||||
IdString clockPort;
|
const auto net_length_plus_one = nd.max_path_length + 1;
|
||||||
TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort);
|
nd.min_remaining_budget = clk_period;
|
||||||
if (portClass == TMG_REGISTER_INPUT || portClass == TMG_ENDPOINT || portClass == TMG_IGNORE) {
|
for (auto &usr : net->users) {
|
||||||
} else {
|
IdString clockPort;
|
||||||
auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t();
|
TimingPortClass portClass = ctx->getPortTimingClass(usr.cell, usr.port, clockPort);
|
||||||
auto budget_override = ctx->getBudgetOverride(net, usr, net_delay);
|
if (portClass == TMG_REGISTER_INPUT || portClass == TMG_ENDPOINT || portClass == TMG_IGNORE) {
|
||||||
auto usr_arrival = net_arrival + net_delay;
|
} else {
|
||||||
// Iterate over all output ports on the same cell as the sink
|
auto net_delay = net_delays ? ctx->getNetinfoRouteDelay(net, usr) : delay_t();
|
||||||
for (auto port : usr.cell->ports) {
|
auto budget_override = ctx->getBudgetOverride(net, usr, net_delay);
|
||||||
if (port.second.type != PORT_OUT || !port.second.net)
|
auto usr_arrival = net_arrival + net_delay;
|
||||||
continue;
|
// Iterate over all output ports on the same cell as the sink
|
||||||
DelayInfo comb_delay;
|
for (auto port : usr.cell->ports) {
|
||||||
// Look up delay through this path
|
if (port.second.type != PORT_OUT || !port.second.net)
|
||||||
bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay);
|
continue;
|
||||||
if (!is_path)
|
DelayInfo comb_delay;
|
||||||
continue;
|
// Look up delay through this path
|
||||||
auto &data = net_data[port.second.net];
|
bool is_path = ctx->getCellDelay(usr.cell, usr.port, port.first, comb_delay);
|
||||||
auto &arrival = data.max_arrival;
|
if (!is_path)
|
||||||
arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay());
|
continue;
|
||||||
if (!budget_override) { // Do not increment path length if budget overriden since it doesn't
|
auto &data = net_data[port.second.net][start_clk];
|
||||||
// require a share of the slack
|
auto &arrival = data.max_arrival;
|
||||||
auto &path_length = data.max_path_length;
|
arrival = std::max(arrival, usr_arrival + comb_delay.maxDelay());
|
||||||
path_length = std::max(path_length, net_length_plus_one);
|
if (!budget_override) { // Do not increment path length if budget overriden since it doesn't
|
||||||
|
// require a share of the slack
|
||||||
|
auto &path_length = data.max_path_length;
|
||||||
|
path_length = std::max(path_length, net_length_plus_one);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const NetInfo *crit_net = nullptr;
|
const NetInfo *crit_net = nullptr;
|
||||||
|
Loading…
Reference in New Issue
Block a user