Skip to content

Commit

Permalink
timing: Move towards DelayPairs for timing reporting (#1359)
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanG077 authored Sep 11, 2024
1 parent 4d1de45 commit 8d0f52f
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 50 deletions.
47 changes: 39 additions & 8 deletions common/kernel/nextpnr_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ struct PortRef
// minimum and maximum delay
struct DelayPair
{
DelayPair(){};
DelayPair() : min_delay(0), max_delay(0) {};
explicit DelayPair(delay_t delay) : min_delay(delay), max_delay(delay) {}
DelayPair(delay_t min_delay, delay_t max_delay) : min_delay(min_delay), max_delay(max_delay) {}
delay_t minDelay() const { return min_delay; }
Expand All @@ -94,13 +94,25 @@ struct DelayPair
{
return {min_delay - other.min_delay, max_delay - other.max_delay};
}
DelayPair &operator+=(const DelayPair &rhs)
{
min_delay += rhs.min_delay;
max_delay += rhs.max_delay;
return *this;
}
DelayPair &operator-=(const DelayPair &rhs)
{
min_delay -= rhs.min_delay;
max_delay -= rhs.max_delay;
return *this;
}
};

// four-quadrant, min and max rise and fall delay
struct DelayQuad
{
DelayPair rise, fall;
DelayQuad() {}
DelayQuad() : rise(0), fall(0) {}
explicit DelayQuad(delay_t delay) : rise(delay), fall(delay) {}
DelayQuad(delay_t min_delay, delay_t max_delay) : rise(min_delay, max_delay), fall(min_delay, max_delay) {}
DelayQuad(DelayPair rise, DelayPair fall) : rise(rise), fall(fall) {}
Expand All @@ -120,6 +132,19 @@ struct DelayQuad

DelayQuad operator+(const DelayQuad &other) const { return {rise + other.rise, fall + other.fall}; }
DelayQuad operator-(const DelayQuad &other) const { return {rise - other.rise, fall - other.fall}; }
DelayQuad &operator+=(const DelayQuad &rhs)
{
rise += rhs.rise;
fall += rhs.fall;
return *this;
}

DelayQuad &operator-=(const DelayQuad &rhs)
{
rise -= rhs.rise;
fall -= rhs.fall;
return *this;
}
};

struct ClockConstraint;
Expand Down Expand Up @@ -200,7 +225,7 @@ struct PseudoCell
virtual bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const = 0;
virtual TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const = 0;
virtual TimingClockingInfo getPortClockingInfo(IdString port, int index) const = 0;
virtual ~PseudoCell(){};
virtual ~PseudoCell() {};
};

struct RegionPlug : PseudoCell
Expand Down Expand Up @@ -336,15 +361,18 @@ struct CriticalPath
// To cell.port
std::pair<IdString, IdString> to;
// Segment delay
delay_t delay;
DelayPair delay;
};

// Clock pair
ClockPair clock_pair;
// Total path delay
delay_t delay;
// Period (max allowed delay)
delay_t period;
DelayPair delay;

// if delay.minDelay() < bound.minDelay() then this is a hold violation
// if delay.maxDelay() > bound.maxDelay() then this is a setup violation
DelayPair bound;

// Individual path segments
std::vector<Segment> segments;
};
Expand All @@ -357,7 +385,7 @@ struct NetSinkTiming
// Cell and port (the sink)
std::pair<IdString, IdString> cell_port;
// Delay
delay_t delay;
DelayPair delay;
};

struct TimingResult
Expand All @@ -379,6 +407,9 @@ struct TimingResult

// Histogram of slack
dict<int, unsigned> slack_histogram;

// TODO: Hold time violations
// dict<IdString, CriticalPath> hold_violations;
};

// Represents the contents of a non-leaf cell in a design
Expand Down
25 changes: 17 additions & 8 deletions common/kernel/report.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ static Json::array json_report_critical_paths(const Context *ctx)
{"port", segment.to.second.c_str(ctx)},
{"loc", Json::array({toLoc.x, toLoc.y})}});

auto segmentJson = Json::object({
{"delay", ctx->getDelayNS(segment.delay)},
{"from", fromJson},
{"to", toJson},
});
auto minDelay = ctx->getDelayNS(segment.delay.minDelay());
auto maxDelay = ctx->getDelayNS(segment.delay.maxDelay());

auto segmentJson =
Json::object({{"delay", Json::array({minDelay, maxDelay})}, {"from", fromJson}, {"to", toJson}});

if (segment.type == CriticalPath::Segment::Type::CLK_TO_Q) {
segmentJson["type"] = "clk-to-q";
Expand Down Expand Up @@ -130,10 +130,13 @@ static Json::array json_report_detailed_net_timings(const Context *ctx)

Json::array endpointsJson;
for (const auto &sink_timing : it.second) {
auto minDelay = ctx->getDelayNS(sink_timing.delay.minDelay());
auto maxDelay = ctx->getDelayNS(sink_timing.delay.maxDelay());

auto endpointJson = Json::object({{"cell", sink_timing.cell_port.first.c_str(ctx)},
{"port", sink_timing.cell_port.second.c_str(ctx)},
{"event", clock_event_name(ctx, sink_timing.clock_pair.end)},
{"delay", ctx->getDelayNS(sink_timing.delay)}});
{"delay", Json::array({minDelay, maxDelay})}});
endpointsJson.push_back(endpointJson);
}

Expand Down Expand Up @@ -191,7 +194,10 @@ Report JSON structure:
},
"type": <path segment type "clk-to-q", "source", "logic", "routing" or "setup">,
"net": <net name (for routing only!)>,
"delay": <segment delay [ns]>,
"delay": [
<minimum segment delay [ns]>,
<maximum segment delay [ns]>,
],
}
...
]
Expand All @@ -209,7 +215,10 @@ Report JSON structure:
"cell": <sink cell name>,
"port": <sink cell port name>,
"event": <destination clock event name>,
"delay": <delay [ns]>,
"delay": [
<minimum segment delay [ns]>,
<maximum segment delay [ns]>,
],
}
...
]
Expand Down
22 changes: 13 additions & 9 deletions common/kernel/timing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,8 @@ void TimingAnalyser::identify_related_domains()

void TimingAnalyser::reset_times()
{
static const auto init_delay =
DelayPair(std::numeric_limits<delay_t>::max(), std::numeric_limits<delay_t>::lowest());
for (auto &port : ports) {
auto do_reset = [&](dict<domain_id_t, ArrivReqTime> &times) {
for (auto &t : times) {
Expand Down Expand Up @@ -758,7 +760,7 @@ void TimingAnalyser::build_detailed_net_timing_report()
sink_timing.clock_pair.end.clock = capture.clock;
sink_timing.clock_pair.end.edge = capture.edge;
sink_timing.cell_port = std::make_pair(pd.cell_port.cell, pd.cell_port.port);
sink_timing.delay = arr.second.value.max_delay;
sink_timing.delay = arr.second.value;

net_timings[net->name].push_back(sink_timing);
}
Expand Down Expand Up @@ -802,23 +804,25 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
auto &launch = domains.at(dp.key.launch).key;
auto &capture = domains.at(dp.key.capture).key;

report.delay = DelayPair(0);

report.clock_pair.start.clock = launch.clock;
report.clock_pair.start.edge = launch.edge;
report.clock_pair.end.clock = capture.clock;
report.clock_pair.end.edge = capture.edge;

report.period = ctx->getDelayFromNS(1.0e9 / ctx->setting<float>("target_freq"));
report.bound = DelayPair(0, ctx->getDelayFromNS(1.0e9 / ctx->setting<float>("target_freq")));
if (launch.edge != capture.edge) {
report.period = report.period / 2;
report.bound.max_delay = report.bound.max_delay / 2;
}

if (!launch.is_async() && ctx->nets.at(launch.clock)->clkconstr) {
if (launch.edge == capture.edge) {
report.period = ctx->nets.at(launch.clock)->clkconstr->period.minDelay();
report.bound.max_delay = ctx->nets.at(launch.clock)->clkconstr->period.minDelay();
} else if (capture.edge == RISING_EDGE) {
report.period = ctx->nets.at(launch.clock)->clkconstr->low.minDelay();
report.bound.max_delay = ctx->nets.at(launch.clock)->clkconstr->low.minDelay();
} else if (capture.edge == FALLING_EDGE) {
report.period = ctx->nets.at(launch.clock)->clkconstr->high.minDelay();
report.bound.max_delay = ctx->nets.at(launch.clock)->clkconstr->high.minDelay();
}
}

Expand Down Expand Up @@ -895,13 +899,13 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
seg_logic.type = CriticalPath::Segment::Type::LOGIC;
}

seg_logic.delay = comb_delay.maxDelay();
seg_logic.delay = comb_delay.delayPair();
seg_logic.from = std::make_pair(last_cell->name, last_port);
seg_logic.to = std::make_pair(driver_cell->name, driver.port);
seg_logic.net = IdString();
report.segments.push_back(seg_logic);

auto net_delay = ctx->getNetinfoRouteDelay(net, sink);
auto net_delay = DelayPair(ctx->getNetinfoRouteDelay(net, sink));

CriticalPath::Segment seg_route;
seg_route.type = CriticalPath::Segment::Type::ROUTING;
Expand All @@ -919,7 +923,7 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
auto sinkClass = ctx->getPortTimingClass(crit_path.back().cell, crit_path.back().port, clockCount);
if (sinkClass == TMG_REGISTER_INPUT && clockCount > 0) {
auto sinkClockInfo = ctx->getPortClockingInfo(crit_path.back().cell, crit_path.back().port, 0);
delay_t setup = sinkClockInfo.setup.maxDelay();
auto setup = sinkClockInfo.setup;

CriticalPath::Segment seg_logic;
seg_logic.type = CriticalPath::Segment::Type::SETUP;
Expand Down
18 changes: 8 additions & 10 deletions common/kernel/timing.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ NEXTPNR_NAMESPACE_BEGIN

struct CellPortKey
{
CellPortKey(){};
CellPortKey(IdString cell, IdString port) : cell(cell), port(port){};
CellPortKey() {};
CellPortKey(IdString cell, IdString port) : cell(cell), port(port) {};
explicit CellPortKey(const PortRef &pr)
{
NPNR_ASSERT(pr.cell != nullptr);
Expand All @@ -49,7 +49,7 @@ struct ClockDomainKey
{
IdString clock;
ClockEdge edge;
ClockDomainKey(IdString clock_net, ClockEdge edge) : clock(clock_net), edge(edge){};
ClockDomainKey(IdString clock_net, ClockEdge edge) : clock(clock_net), edge(edge) {};
// probably also need something here to deal with constraints
inline bool is_async() const { return clock == IdString(); }

Expand All @@ -63,7 +63,7 @@ typedef int domain_id_t;
struct ClockDomainPairKey
{
domain_id_t launch, capture;
ClockDomainPairKey(domain_id_t launch, domain_id_t capture) : launch(launch), capture(capture){};
ClockDomainPairKey(domain_id_t launch, domain_id_t capture) : launch(launch), capture(capture) {};
inline bool operator==(const ClockDomainPairKey &other) const
{
return (launch == other.launch) && (capture == other.capture);
Expand Down Expand Up @@ -128,8 +128,6 @@ struct TimingAnalyser
// get the N worst endpoints for a given domain pair
std::vector<CellPortKey> get_worst_eps(domain_id_t domain_pair, int count);

const DelayPair init_delay{std::numeric_limits<delay_t>::max(), std::numeric_limits<delay_t>::lowest()};

// Set arrival/required times if more/less than the current value
void set_arrival_time(CellPortKey target, domain_id_t domain, DelayPair arrival, int path_length,
CellPortKey prev = CellPortKey());
Expand Down Expand Up @@ -174,9 +172,9 @@ struct TimingAnalyser
ClockEdge edge;

CellArc(ArcType type, IdString other_port, DelayQuad value)
: type(type), other_port(other_port), value(value), edge(RISING_EDGE){};
: 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){};
: type(type), other_port(other_port), value(value), edge(edge) {};
};

// Timing data for every cell port
Expand All @@ -200,15 +198,15 @@ struct TimingAnalyser

struct PerDomain
{
PerDomain(ClockDomainKey key) : key(key){};
PerDomain(ClockDomainKey key) : key(key) {};
ClockDomainKey key;
// these are pairs (signal port; clock port)
std::vector<std::pair<CellPortKey, IdString>> startpoints, endpoints;
};

struct PerDomainPair
{
PerDomainPair(ClockDomainPairKey key) : key(key){};
PerDomainPair(ClockDomainPairKey key) : key(key) {};
ClockDomainPairKey key;
DelayPair period{0};
delay_t worst_setup_slack, worst_hold_slack;
Expand Down
Loading

0 comments on commit 8d0f52f

Please sign in to comment.