Skip to content

Commit

Permalink
WIP timing: integrate c2c delays
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanG077 committed Sep 13, 2024
1 parent 57f014e commit 23d4f13
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 57 deletions.
10 changes: 5 additions & 5 deletions common/kernel/context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ delay_t Context::predictArcDelay(const NetInfo *net_info, const PortRef &sink) c

delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &user_info) const
{
// #ifdef ARCH_ECP5
// if (net_info->is_global)
// return 0;
// #endif
#ifdef ARCH_ECP5
if (net_info->is_global)
return 0;
#endif

if (net_info->wires.empty())
return predictArcDelay(net_info, user_info);
Expand Down Expand Up @@ -417,7 +417,7 @@ void Context::check() const
namespace {
struct FixupHierarchyWorker
{
FixupHierarchyWorker(Context *ctx) : ctx(ctx) {};
FixupHierarchyWorker(Context *ctx) : ctx(ctx){};
Context *ctx;
void run()
{
Expand Down
12 changes: 9 additions & 3 deletions common/kernel/nextpnr_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ struct PortRef
// minimum and maximum delay
struct DelayPair
{
DelayPair() : min_delay(0), max_delay(0) {};
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 Down Expand Up @@ -278,7 +278,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 @@ -398,16 +398,20 @@ struct CriticalPath
// Segment type
enum class Type
{
CLK_SKEW, // Clock skew
CLK_TO_Q, // Clock-to-Q delay
SOURCE, // Delayless source
LOGIC, // Combinational logic delay
ROUTING, // Routing delay
SETUP // Setup time in sink
SETUP, // Setup time in sink
HOLD // Hold time in sink
};

[[maybe_unused]] static const std::string type_to_str(Type typ)
{
switch (typ) {
case Type::CLK_SKEW:
return "CLK_SKEW";
case Type::CLK_TO_Q:
return "CLK_TO_Q";
case Type::SOURCE:
Expand All @@ -418,6 +422,8 @@ struct CriticalPath
return "ROUTING";
case Type::SETUP:
return "SETUP";
case Type::HOLD:
return "HOLD";
default:
log_error("Impossible Segment::Type");
}
Expand Down
176 changes: 134 additions & 42 deletions common/kernel/timing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -582,9 +582,10 @@ void TimingAnalyser::walk_forward()
for (auto &fanin : pd.cell_arcs) {
if (fanin.type == CellArc::CLK_TO_Q && fanin.other_port == sp.second) {
init_arrival += fanin.value.delayPair();
// Include the clock delay if clock_skew analysis is enabled
if (clock_skew) {
auto clock_delay = ports.at(CellPortKey(sp.first.cell, fanin.other_port)).route_delay;
init_arrival += clock_delay;
init_arrival -= clock_delay;
}
break;
}
Expand Down Expand Up @@ -638,13 +639,14 @@ void TimingAnalyser::walk_backward()
if (ep.second != IdString()) {
// Add setup/hold time, if this endpoint is clocked
for (auto &fanin : pd.cell_arcs) {
printf("walk bwd %s.%s, fanin: %s, arctype: %s\n", ep.first.cell.c_str(ctx),
ep.first.port.c_str(ctx), fanin.other_port.c_str(ctx), arcType_to_str(fanin.type).c_str());

if (fanin.type == CellArc::SETUP && fanin.other_port == ep.second) {
if (clock_skew) {
auto clock_delay = ports.at(CellPortKey(ep.first.cell, fanin.other_port)).route_delay;
init_required += clock_delay;
printf("walk bwd %s.%s, setup: %f, clock delay: %f\n", ep.first.cell.c_str(ctx),
ep.first.port.c_str(ctx), ctx->getDelayNS(fanin.value.maxDelay()),
ctx->getDelayNS(clock_delay.maxDelay()));
}
init_required.min_delay -= fanin.value.maxDelay();
}
Expand Down Expand Up @@ -825,9 +827,12 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
{
CriticalPath report;

auto &dp = domain_pairs.at(domain_pair);
auto &launch = domains.at(dp.key.launch).key;
auto &capture = domains.at(dp.key.capture).key;
const auto &dp = domain_pairs.at(domain_pair);
const auto &launch = domains.at(dp.key.launch).key;
const auto &capture = domains.at(dp.key.capture).key;

printf("launch clock name: %s\n", launch.clock.c_str(ctx));
printf("capture clock name: %s\n", capture.clock.c_str(ctx));

report.delay = DelayPair(0);

Expand Down Expand Up @@ -867,21 +872,30 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
}
}

// Walk the min or max path backwards to find a single crit path
pool<std::pair<IdString, IdString>> visited;
std::vector<PortRef> crit_path_rev;
auto cursor = endpoint;

while (cursor != CellPortKey()) {
printf("walking crit path\n");
bool is_startpoint = false;
do {
auto cell = cell_info(cursor);
auto &port = port_info(cursor);
int port_clocks;
auto portClass = ctx->getPortTimingClass(cell, port.name, port_clocks);
printf("\t%s %s - %s\n", cell->name.c_str(ctx), port.name.c_str(ctx),
timingPortClass_to_str(portClass).c_str());

// combinational loop
if (!visited.insert(std::make_pair(cell->name, port.name)).second)
break;

if (portClass != TMG_CLOCK_INPUT && portClass != TMG_IGNORE && port.type == PortType::PORT_IN)
// Exclude clock inputs because we use `TMG_REGISTER_OUTPUT` as startpoitn
auto is_input = portClass != TMG_CLOCK_INPUT && portClass != TMG_IGNORE && port.type == PortType::PORT_IN;
is_startpoint = portClass == TMG_REGISTER_OUTPUT || portClass == TMG_STARTPOINT;

if (is_input || is_startpoint)
crit_path_rev.emplace_back(PortRef{cell, port.name});

if (!ports.at(cursor).arrival.count(dp.key.launch))
Expand All @@ -892,32 +906,109 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
} else {
cursor = ports.at(cursor).arrival.at(dp.key.launch).bwd_min;
}
}
} while (!is_startpoint);

auto crit_path = boost::adaptors::reverse(crit_path_rev);

auto &front = crit_path.front();
auto &front_port = front.cell->ports.at(front.port);
auto &front_driver = front_port.net->driver;
// Get timing and clocking info on the startpoint
const auto &sp = crit_path.front();
const auto &sp_cell = sp.cell;
const auto &sp_port = sp_cell->ports.at(sp.port);
int sp_clocks;
const auto sp_portClass = ctx->getPortTimingClass(sp_cell, sp_port.name, sp_clocks);
TimingClockingInfo sp_clk_info;
const NetInfo *sp_clk_net = nullptr;
bool register_start = sp_portClass == TMG_REGISTER_OUTPUT;

printf("register_start %d, tmg: %s\n", register_start, timingPortClass_to_str(sp_portClass).c_str());

if (register_start) {
// If we don't find a clock we don't consider this startpoint to be registered.
register_start = false;
for (int i = 0; i < sp_clocks; i++) {
sp_clk_info = ctx->getPortClockingInfo(sp_cell, sp_port.name, i);
const auto clk_net = sp_cell->getPort(sp_clk_info.clock_port);
printf("clk_net null: %d, net name: %s launch name: %s, edge info: %s, edge launch: %s\n",
clk_net == nullptr, clk_net->name.c_str(ctx), launch.clock.c_str(ctx),
clockEdge_to_str(sp_clk_info.edge).c_str(), clockEdge_to_str(launch.edge).c_str());
if (clk_net != nullptr && clk_net->name == launch.clock && sp_clk_info.edge == launch.edge) {
sp_clk_net = clk_net;
register_start = true;
break;
}
}
}

portClass = ctx->getPortTimingClass(front_driver.cell, front_driver.port, port_clocks);
// Get timing and clocking info on the endpoint
const auto &ep = crit_path.back();
const auto &ep_cell = ep.cell;
const auto &ep_port = ep_cell->ports.at(ep.port);
int ep_clocks;
const auto ep_portClass = ctx->getPortTimingClass(ep_cell, ep_port.name, ep_clocks);
TimingClockingInfo ep_clk_info;
const NetInfo *ep_clk_net = nullptr;

bool register_end = ep_portClass == TMG_REGISTER_INPUT;
printf("register_end %d, tmg: %s\n", register_end, timingPortClass_to_str(ep_portClass).c_str());

if (register_end) {
// If we don't find a clock we don't consider this startpoint to be registered.
register_end = false;
for (int i = 0; i < ep_clocks; i++) {
ep_clk_info = ctx->getPortClockingInfo(ep_cell, ep_port.name, i);
const auto clk_net = ep_cell->getPort(ep_clk_info.clock_port);
printf("clk_net null: %d, net name: %s capture name: %s, edge info: %s, edge capture: %s\n",
clk_net == nullptr, clk_net->name.c_str(ctx), capture.clock.c_str(ctx),
clockEdge_to_str(ep_clk_info.edge).c_str(), clockEdge_to_str(capture.edge).c_str());
if (clk_net != nullptr && clk_net->name == capture.clock && ep_clk_info.edge == capture.edge) {
ep_clk_net = clk_net;
register_end = true;
break;
}
}
}

const CellInfo *last_cell = front.cell;
IdString last_port = front_driver.port;
auto clock_pair = std::make_pair(launch.clock, capture.clock);
auto related_clock = clock_delays.count(clock_pair) > 0;
auto same_clock = launch.clock == capture.clock;

// Calculate clock skew only if start- and endpoint are registers
// and either they are the same or related clock
printf("clock_skew %d, register_start %d, register_end %d, same_clock: %d, related_clock: %d\n", clock_skew,
register_start, register_end, same_clock, related_clock);
if (clock_skew && register_start && register_end && (same_clock || related_clock)) {
delay_t c2c_delay = 0;
if (related_clock) {
c2c_delay = clock_delays.at(clock_pair);
}

int clock_start = -1;
if (portClass == TMG_REGISTER_OUTPUT) {
for (int i = 0; i < port_clocks; i++) {
TimingClockingInfo clockInfo = ctx->getPortClockingInfo(front_driver.cell, front_driver.port, i);
const NetInfo *clknet = front_driver.cell->getPort(clockInfo.clock_port);
if (clknet != nullptr && clknet->name == launch.clock && clockInfo.edge == launch.edge) {
last_port = clockInfo.clock_port;
clock_start = i;
break;
auto clock_delay_launch = ctx->getNetinfoRouteDelay(sp_clk_net, PortRef{sp_cell, sp_clk_info.clock_port});
auto clock_delay_capture = ctx->getNetinfoRouteDelay(ep_clk_net, PortRef{ep_cell, ep_clk_info.clock_port});

auto skew = clock_delay_launch - clock_delay_capture - c2c_delay;
printf("delay launch: %f\n", ctx->getDelayNS(clock_delay_launch));
printf("delay capture: %f\n", ctx->getDelayNS(clock_delay_capture));
printf("c2c: %f\n", ctx->getDelayNS(c2c_delay));
// If skew is zero it just pollutes the report
if (skew != 0) {
CriticalPath::Segment seg_skew;
seg_skew.type = CriticalPath::Segment::Type::CLK_SKEW;
seg_skew.delay = DelayPair(clock_delay_launch - clock_delay_capture - c2c_delay);
seg_skew.from = std::make_pair(sp_cell->name, sp_clk_info.clock_port);
seg_skew.to = std::make_pair(ep_cell->name, ep_clk_info.clock_port);
if (same_clock) {
seg_skew.net = launch.clock;
} else {
seg_skew.net = IdString();
}
report.segments.push_back(seg_skew);
}
}

// Track last cell and port traversed
const CellInfo *prev_cell = sp_cell;
IdString prev_port = sp_port.name;

for (auto sink : crit_path) {
auto sink_cell = sink.cell;
auto &port = sink_cell->ports.at(sink.port);
Expand All @@ -928,22 +1019,20 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
CriticalPath::Segment seg_logic;

DelayQuad comb_delay;
if (clock_start != -1) {
auto clockInfo = ctx->getPortClockingInfo(driver_cell, driver.port, clock_start);
comb_delay = clockInfo.clockToQ;
clock_start = -1;
if (is_startpoint && register_start) {
comb_delay = sp_clk_info.clockToQ;
seg_logic.type = CriticalPath::Segment::Type::CLK_TO_Q;
} else if (last_port == driver.port) {
} else if (is_startpoint) {
// Case where we start with a STARTPOINT etc
comb_delay = DelayQuad(0);
seg_logic.type = CriticalPath::Segment::Type::SOURCE;
} else {
ctx->getCellDelay(driver_cell, last_port, driver.port, comb_delay);
ctx->getCellDelay(driver_cell, prev_port, driver.port, comb_delay);
seg_logic.type = CriticalPath::Segment::Type::LOGIC;
}

seg_logic.delay = comb_delay.delayPair();
seg_logic.from = std::make_pair(last_cell->name, last_port);
seg_logic.from = std::make_pair(prev_cell->name, prev_port);
seg_logic.to = std::make_pair(driver_cell->name, driver.port);
seg_logic.net = IdString();
report.segments.push_back(seg_logic);
Expand All @@ -958,20 +1047,23 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
seg_route.net = net->name;
report.segments.push_back(seg_route);

last_cell = sink_cell;
last_port = sink.port;
prev_cell = sink_cell;
prev_port = sink.port;
is_startpoint = false;
}

int clockCount = 0;
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);
auto setup = sinkClockInfo.setup;

// Finally add setup time
if (register_end) {
CriticalPath::Segment seg_logic;
seg_logic.type = CriticalPath::Segment::Type::SETUP;
seg_logic.delay = setup;
seg_logic.from = std::make_pair(last_cell->name, last_port);
seg_logic.delay = DelayPair(0);
if (longest_path) {
seg_logic.type = CriticalPath::Segment::Type::SETUP;
seg_logic.delay += ep_clk_info.setup;
} else {
seg_logic.type = CriticalPath::Segment::Type::HOLD;
seg_logic.delay -= ep_clk_info.hold;
}
seg_logic.from = std::make_pair(prev_cell->name, prev_port);
seg_logic.to = seg_logic.from;
seg_logic.net = IdString();
report.segments.push_back(seg_logic);
Expand Down
16 changes: 9 additions & 7 deletions common/kernel/timing_log.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,15 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
if (segment.type == CriticalPath::Segment::Type::CLK_TO_Q ||
segment.type == CriticalPath::Segment::Type::SOURCE ||
segment.type == CriticalPath::Segment::Type::LOGIC ||
segment.type == CriticalPath::Segment::Type::SETUP) {
segment.type == CriticalPath::Segment::Type::SETUP ||
segment.type == CriticalPath::Segment::Type::HOLD) {
logic_total += segment.delay;

const std::string type_name = (segment.type == CriticalPath::Segment::Type::SETUP) ? "Setup" : "Source";

log_info("%4.1f %4.1f %s %s.%s\n", get_delay_ns(segment.delay), get_delay_ns(total), type_name.c_str(),
segment.to.first.c_str(ctx), segment.to.second.c_str(ctx));
} else if (segment.type == CriticalPath::Segment::Type::ROUTING) {
log_info("%6.3f %6.3f %s %s.%s\n", get_delay_ns(segment.delay), get_delay_ns(total),
CriticalPath::Segment::type_to_str(segment.type).c_str(), segment.to.first.c_str(ctx),
segment.to.second.c_str(ctx));
} else if (segment.type == CriticalPath::Segment::Type::ROUTING ||
segment.type == CriticalPath::Segment::Type::CLK_SKEW) {
route_total = route_total + segment.delay;

const auto &driver = ctx->cells.at(segment.from.first);
Expand All @@ -106,7 +107,8 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
auto driver_loc = ctx->getBelLocation(driver->bel);
auto sink_loc = ctx->getBelLocation(sink->bel);

log_info("%4.1f %4.1f Net %s (%d,%d) -> (%d,%d)\n", get_delay_ns(segment.delay), get_delay_ns(total),
log_info("%6.3f %6.3f %s Net %s (%d,%d) -> (%d,%d)\n", get_delay_ns(segment.delay),
get_delay_ns(total), CriticalPath::Segment::type_to_str(segment.type).c_str(),
segment.net.c_str(ctx), driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y);
log_info(" Sink %s.%s\n", segment.to.first.c_str(ctx), segment.to.second.c_str(ctx));

Expand Down

0 comments on commit 23d4f13

Please sign in to comment.