timing: integrate c2c delays and cleanup code
This commit is contained in:
parent
fc3b2de8da
commit
86106cb49a
@ -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
|
delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &user_info) const
|
||||||
{
|
{
|
||||||
// #ifdef ARCH_ECP5
|
#ifdef ARCH_ECP5
|
||||||
// if (net_info->is_global)
|
if (net_info->is_global)
|
||||||
// return 0;
|
return 0;
|
||||||
// #endif
|
#endif
|
||||||
|
|
||||||
if (net_info->wires.empty())
|
if (net_info->wires.empty())
|
||||||
return predictArcDelay(net_info, user_info);
|
return predictArcDelay(net_info, user_info);
|
||||||
@ -417,7 +417,7 @@ void Context::check() const
|
|||||||
namespace {
|
namespace {
|
||||||
struct FixupHierarchyWorker
|
struct FixupHierarchyWorker
|
||||||
{
|
{
|
||||||
FixupHierarchyWorker(Context *ctx) : ctx(ctx) {};
|
FixupHierarchyWorker(Context *ctx) : ctx(ctx){};
|
||||||
Context *ctx;
|
Context *ctx;
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
|
@ -81,7 +81,7 @@ struct PortRef
|
|||||||
// minimum and maximum delay
|
// minimum and maximum delay
|
||||||
struct DelayPair
|
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) {}
|
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) {}
|
DelayPair(delay_t min_delay, delay_t max_delay) : min_delay(min_delay), max_delay(max_delay) {}
|
||||||
delay_t minDelay() const { return min_delay; }
|
delay_t minDelay() const { return min_delay; }
|
||||||
@ -278,7 +278,7 @@ struct PseudoCell
|
|||||||
virtual bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const = 0;
|
virtual bool getDelay(IdString fromPort, IdString toPort, DelayQuad &delay) const = 0;
|
||||||
virtual TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const = 0;
|
virtual TimingPortClass getPortTimingClass(IdString port, int &clockInfoCount) const = 0;
|
||||||
virtual TimingClockingInfo getPortClockingInfo(IdString port, int index) const = 0;
|
virtual TimingClockingInfo getPortClockingInfo(IdString port, int index) const = 0;
|
||||||
virtual ~PseudoCell() {};
|
virtual ~PseudoCell(){};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RegionPlug : PseudoCell
|
struct RegionPlug : PseudoCell
|
||||||
@ -398,16 +398,20 @@ struct CriticalPath
|
|||||||
// Segment type
|
// Segment type
|
||||||
enum class Type
|
enum class Type
|
||||||
{
|
{
|
||||||
|
CLK_SKEW, // Clock skew
|
||||||
CLK_TO_Q, // Clock-to-Q delay
|
CLK_TO_Q, // Clock-to-Q delay
|
||||||
SOURCE, // Delayless source
|
SOURCE, // Delayless source
|
||||||
LOGIC, // Combinational logic delay
|
LOGIC, // Combinational logic delay
|
||||||
ROUTING, // Routing 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)
|
[[maybe_unused]] static const std::string type_to_str(Type typ)
|
||||||
{
|
{
|
||||||
switch (typ) {
|
switch (typ) {
|
||||||
|
case Type::CLK_SKEW:
|
||||||
|
return "CLK_SKEW";
|
||||||
case Type::CLK_TO_Q:
|
case Type::CLK_TO_Q:
|
||||||
return "CLK_TO_Q";
|
return "CLK_TO_Q";
|
||||||
case Type::SOURCE:
|
case Type::SOURCE:
|
||||||
@ -418,6 +422,8 @@ struct CriticalPath
|
|||||||
return "ROUTING";
|
return "ROUTING";
|
||||||
case Type::SETUP:
|
case Type::SETUP:
|
||||||
return "SETUP";
|
return "SETUP";
|
||||||
|
case Type::HOLD:
|
||||||
|
return "HOLD";
|
||||||
default:
|
default:
|
||||||
log_error("Impossible Segment::Type");
|
log_error("Impossible Segment::Type");
|
||||||
}
|
}
|
||||||
|
@ -175,19 +175,10 @@ void TimingAnalyser::get_route_delays()
|
|||||||
if (ni->driver.cell == nullptr || ni->driver.cell->bel == BelId())
|
if (ni->driver.cell == nullptr || ni->driver.cell->bel == BelId())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (clock_skew) {
|
|
||||||
printf("net %s has driver %s.%s\n", ni->name.c_str(ctx), ni->driver.cell->name.c_str(ctx),
|
|
||||||
ni->driver.port.c_str(ctx));
|
|
||||||
}
|
|
||||||
for (auto &usr : ni->users) {
|
for (auto &usr : ni->users) {
|
||||||
if (usr.cell->bel == BelId())
|
if (usr.cell->bel == BelId())
|
||||||
continue;
|
continue;
|
||||||
ports.at(CellPortKey(usr)).route_delay = DelayPair(ctx->getNetinfoRouteDelay(ni, usr));
|
ports.at(CellPortKey(usr)).route_delay = DelayPair(ctx->getNetinfoRouteDelay(ni, usr));
|
||||||
|
|
||||||
if (clock_skew) {
|
|
||||||
printf("\tuser %s.%s, delay: %f\n", usr.cell->name.c_str(ctx), usr.port.c_str(ctx),
|
|
||||||
ctx->getDelayNS(ctx->getNetinfoRouteDelay(ni, usr)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -582,9 +573,10 @@ void TimingAnalyser::walk_forward()
|
|||||||
for (auto &fanin : pd.cell_arcs) {
|
for (auto &fanin : pd.cell_arcs) {
|
||||||
if (fanin.type == CellArc::CLK_TO_Q && fanin.other_port == sp.second) {
|
if (fanin.type == CellArc::CLK_TO_Q && fanin.other_port == sp.second) {
|
||||||
init_arrival += fanin.value.delayPair();
|
init_arrival += fanin.value.delayPair();
|
||||||
if (clock_skew) {
|
// Include the clock delay if clock_skew analysis is enabled
|
||||||
|
if (with_clock_skew) {
|
||||||
auto clock_delay = ports.at(CellPortKey(sp.first.cell, fanin.other_port)).route_delay;
|
auto clock_delay = ports.at(CellPortKey(sp.first.cell, fanin.other_port)).route_delay;
|
||||||
init_arrival += clock_delay;
|
init_arrival += DelayPair(clock_delay.min_delay);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -638,13 +630,11 @@ void TimingAnalyser::walk_backward()
|
|||||||
if (ep.second != IdString()) {
|
if (ep.second != IdString()) {
|
||||||
// Add setup/hold time, if this endpoint is clocked
|
// Add setup/hold time, if this endpoint is clocked
|
||||||
for (auto &fanin : pd.cell_arcs) {
|
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 (fanin.type == CellArc::SETUP && fanin.other_port == ep.second) {
|
||||||
if (clock_skew) {
|
if (with_clock_skew) {
|
||||||
auto clock_delay = ports.at(CellPortKey(ep.first.cell, fanin.other_port)).route_delay;
|
auto clock_delay = ports.at(CellPortKey(ep.first.cell, fanin.other_port)).route_delay;
|
||||||
init_required += clock_delay;
|
init_required -= DelayPair(clock_delay.min_delay);
|
||||||
}
|
}
|
||||||
init_required.min_delay -= fanin.value.maxDelay();
|
init_required.min_delay -= fanin.value.maxDelay();
|
||||||
}
|
}
|
||||||
@ -695,6 +685,9 @@ dict<domain_id_t, delay_t> TimingAnalyser::max_delay_by_domain_pairs()
|
|||||||
auto dp = domain_pair_id(launch, capture);
|
auto dp = domain_pair_id(launch, capture);
|
||||||
|
|
||||||
delay_t delay = arr.second.value.maxDelay() - req.second.value.minDelay();
|
delay_t delay = arr.second.value.maxDelay() - req.second.value.minDelay();
|
||||||
|
printf("%s -> %s, arr: %f, req: %f, delay: %f\n", domains.at(launch).key.clock.c_str(ctx),
|
||||||
|
domains.at(capture).key.clock.c_str(ctx), ctx->getDelayNS(arr.second.value.maxDelay()),
|
||||||
|
ctx->getDelayNS(req.second.value.minDelay()), ctx->getDelayNS(delay));
|
||||||
if (!domain_delay.count(dp) || domain_delay.at(dp) < delay)
|
if (!domain_delay.count(dp) || domain_delay.at(dp) < delay)
|
||||||
domain_delay[dp] = delay;
|
domain_delay[dp] = delay;
|
||||||
}
|
}
|
||||||
@ -825,9 +818,9 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
|||||||
{
|
{
|
||||||
CriticalPath report;
|
CriticalPath report;
|
||||||
|
|
||||||
auto &dp = domain_pairs.at(domain_pair);
|
const auto &dp = domain_pairs.at(domain_pair);
|
||||||
auto &launch = domains.at(dp.key.launch).key;
|
const auto &launch = domains.at(dp.key.launch).key;
|
||||||
auto &capture = domains.at(dp.key.capture).key;
|
const auto &capture = domains.at(dp.key.capture).key;
|
||||||
|
|
||||||
report.delay = DelayPair(0);
|
report.delay = DelayPair(0);
|
||||||
|
|
||||||
@ -851,27 +844,13 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set hold time
|
// Walk the min or max path backwards to find a single crit path
|
||||||
auto cell = cell_info(endpoint);
|
|
||||||
auto &port = port_info(endpoint);
|
|
||||||
int port_clocks;
|
|
||||||
auto portClass = ctx->getPortTimingClass(cell, port.name, port_clocks);
|
|
||||||
if (portClass == TMG_REGISTER_INPUT) {
|
|
||||||
for (int i = 0; i < port_clocks; i++) {
|
|
||||||
auto info = ctx->getPortClockingInfo(cell, port.name, i);
|
|
||||||
if (!cell->ports.count(info.clock_port) || cell->ports.at(info.clock_port).net == nullptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
report.bound.min_delay = info.hold.min_delay;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pool<std::pair<IdString, IdString>> visited;
|
pool<std::pair<IdString, IdString>> visited;
|
||||||
std::vector<PortRef> crit_path_rev;
|
std::vector<PortRef> crit_path_rev;
|
||||||
auto cursor = endpoint;
|
auto cursor = endpoint;
|
||||||
|
|
||||||
while (cursor != CellPortKey()) {
|
bool is_startpoint = false;
|
||||||
|
do {
|
||||||
auto cell = cell_info(cursor);
|
auto cell = cell_info(cursor);
|
||||||
auto &port = port_info(cursor);
|
auto &port = port_info(cursor);
|
||||||
int port_clocks;
|
int port_clocks;
|
||||||
@ -881,7 +860,12 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
|||||||
if (!visited.insert(std::make_pair(cell->name, port.name)).second)
|
if (!visited.insert(std::make_pair(cell->name, port.name)).second)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (portClass != TMG_CLOCK_INPUT && portClass != TMG_IGNORE && port.type == PortType::PORT_IN)
|
// We store the reversed critical path as all input ports that lead to
|
||||||
|
// the timing startpoint.
|
||||||
|
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});
|
crit_path_rev.emplace_back(PortRef{cell, port.name});
|
||||||
|
|
||||||
if (!ports.at(cursor).arrival.count(dp.key.launch))
|
if (!ports.at(cursor).arrival.count(dp.key.launch))
|
||||||
@ -892,32 +876,99 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
|||||||
} else {
|
} else {
|
||||||
cursor = ports.at(cursor).arrival.at(dp.key.launch).bwd_min;
|
cursor = ports.at(cursor).arrival.at(dp.key.launch).bwd_min;
|
||||||
}
|
}
|
||||||
}
|
} while (!is_startpoint);
|
||||||
|
|
||||||
auto crit_path = boost::adaptors::reverse(crit_path_rev);
|
auto crit_path = boost::adaptors::reverse(crit_path_rev);
|
||||||
|
|
||||||
auto &front = crit_path.front();
|
// Get timing and clocking info on the startpoint
|
||||||
auto &front_port = front.cell->ports.at(front.port);
|
const auto &sp = crit_path.front();
|
||||||
auto &front_driver = front_port.net->driver;
|
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;
|
||||||
|
|
||||||
portClass = ctx->getPortTimingClass(front_driver.cell, front_driver.port, port_clocks);
|
if (register_start) {
|
||||||
|
// If we don't find a clock we don't consider this startpoint to be registered.
|
||||||
const CellInfo *last_cell = front.cell;
|
register_start = sp_clocks > 0;
|
||||||
IdString last_port = front_driver.port;
|
for (int i = 0; i < sp_clocks; i++) {
|
||||||
|
sp_clk_info = ctx->getPortClockingInfo(sp_cell, sp_port.name, i);
|
||||||
int clock_start = -1;
|
const auto clk_net = sp_cell->getPort(sp_clk_info.clock_port);
|
||||||
if (portClass == TMG_REGISTER_OUTPUT) {
|
register_start = clk_net != nullptr && clk_net->name == launch.clock && sp_clk_info.edge == launch.edge;
|
||||||
for (int i = 0; i < port_clocks; i++) {
|
if (register_start) {
|
||||||
TimingClockingInfo clockInfo = ctx->getPortClockingInfo(front_driver.cell, front_driver.port, i);
|
sp_clk_net = clk_net;
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
if (register_end) {
|
||||||
|
// If we don't find a clock we don't consider this startpoint to be registered.
|
||||||
|
register_end = ep_clocks > 0;
|
||||||
|
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);
|
||||||
|
|
||||||
|
register_end = clk_net != nullptr && clk_net->name == capture.clock && ep_clk_info.edge == capture.edge;
|
||||||
|
if (register_end) {
|
||||||
|
ep_clk_net = clk_net;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
delay_t skew = 0;
|
||||||
|
if (related_clock) {
|
||||||
|
skew -= clock_delays.at(clock_pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate clock skew only if start- and endpoint are registered
|
||||||
|
// and either the clock domains are the same or related clock
|
||||||
|
if (with_clock_skew && register_start && register_end && (same_clock || related_clock)) {
|
||||||
|
|
||||||
|
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});
|
||||||
|
|
||||||
|
printf("delay launch: %f\n", ctx->getDelayNS(clock_delay_launch));
|
||||||
|
printf("delay capture: %f\n", ctx->getDelayNS(clock_delay_capture));
|
||||||
|
printf("clock to clock: %f\n", ctx->getDelayNS(skew));
|
||||||
|
skew += clock_delay_launch - clock_delay_capture;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skew != 0) {
|
||||||
|
CriticalPath::Segment seg_skew;
|
||||||
|
seg_skew.type = CriticalPath::Segment::Type::CLK_SKEW;
|
||||||
|
seg_skew.delay = DelayPair(skew);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
const CellInfo *prev_cell = sp_cell;
|
||||||
|
IdString prev_port = sp_port.name;
|
||||||
|
|
||||||
for (auto sink : crit_path) {
|
for (auto sink : crit_path) {
|
||||||
auto sink_cell = sink.cell;
|
auto sink_cell = sink.cell;
|
||||||
auto &port = sink_cell->ports.at(sink.port);
|
auto &port = sink_cell->ports.at(sink.port);
|
||||||
@ -928,22 +979,20 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
|||||||
CriticalPath::Segment seg_logic;
|
CriticalPath::Segment seg_logic;
|
||||||
|
|
||||||
DelayQuad comb_delay;
|
DelayQuad comb_delay;
|
||||||
if (clock_start != -1) {
|
if (is_startpoint && register_start) {
|
||||||
auto clockInfo = ctx->getPortClockingInfo(driver_cell, driver.port, clock_start);
|
comb_delay = sp_clk_info.clockToQ;
|
||||||
comb_delay = clockInfo.clockToQ;
|
|
||||||
clock_start = -1;
|
|
||||||
seg_logic.type = CriticalPath::Segment::Type::CLK_TO_Q;
|
seg_logic.type = CriticalPath::Segment::Type::CLK_TO_Q;
|
||||||
} else if (last_port == driver.port) {
|
} else if (prev_port == driver.port) {
|
||||||
// Case where we start with a STARTPOINT etc
|
// Case where we start with a STARTPOINT etc
|
||||||
comb_delay = DelayQuad(0);
|
comb_delay = DelayQuad(0);
|
||||||
seg_logic.type = CriticalPath::Segment::Type::SOURCE;
|
seg_logic.type = CriticalPath::Segment::Type::SOURCE;
|
||||||
} else {
|
} 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.type = CriticalPath::Segment::Type::LOGIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
seg_logic.delay = comb_delay.delayPair();
|
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.to = std::make_pair(driver_cell->name, driver.port);
|
||||||
seg_logic.net = IdString();
|
seg_logic.net = IdString();
|
||||||
report.segments.push_back(seg_logic);
|
report.segments.push_back(seg_logic);
|
||||||
@ -958,23 +1007,29 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
|||||||
seg_route.net = net->name;
|
seg_route.net = net->name;
|
||||||
report.segments.push_back(seg_route);
|
report.segments.push_back(seg_route);
|
||||||
|
|
||||||
last_cell = sink_cell;
|
prev_cell = sink_cell;
|
||||||
last_port = sink.port;
|
prev_port = sink.port;
|
||||||
|
is_startpoint = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int clockCount = 0;
|
// Add setup/hold time as final segment
|
||||||
auto sinkClass = ctx->getPortTimingClass(crit_path.back().cell, crit_path.back().port, clockCount);
|
// And add hold time as min bound
|
||||||
if (sinkClass == TMG_REGISTER_INPUT && clockCount > 0) {
|
if (register_end) {
|
||||||
auto sinkClockInfo = ctx->getPortClockingInfo(crit_path.back().cell, crit_path.back().port, 0);
|
|
||||||
auto setup = sinkClockInfo.setup;
|
|
||||||
|
|
||||||
CriticalPath::Segment seg_logic;
|
CriticalPath::Segment seg_logic;
|
||||||
|
seg_logic.delay = DelayPair(0);
|
||||||
|
if (longest_path) {
|
||||||
seg_logic.type = CriticalPath::Segment::Type::SETUP;
|
seg_logic.type = CriticalPath::Segment::Type::SETUP;
|
||||||
seg_logic.delay = setup;
|
seg_logic.delay += ep_clk_info.setup;
|
||||||
seg_logic.from = std::make_pair(last_cell->name, last_port);
|
} 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.to = seg_logic.from;
|
||||||
seg_logic.net = IdString();
|
seg_logic.net = IdString();
|
||||||
report.segments.push_back(seg_logic);
|
report.segments.push_back(seg_logic);
|
||||||
|
|
||||||
|
report.bound.min_delay = ep_clk_info.hold.min_delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
return report;
|
return report;
|
||||||
@ -1116,17 +1171,6 @@ std::vector<CriticalPath> TimingAnalyser::get_min_delay_violations()
|
|||||||
const auto &capture = domains.at(capture_id);
|
const auto &capture = domains.at(capture_id);
|
||||||
const auto &capture_clock = capture.key.clock;
|
const auto &capture_clock = capture.key.clock;
|
||||||
|
|
||||||
if (ctx->nets.count(capture_clock) == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
NetInfo *clk_net = ctx->nets.at(capture_clock).get();
|
|
||||||
if (clk_net->clkconstr == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
DelayPair period = clk_net->clkconstr->period;
|
|
||||||
|
|
||||||
for (auto &ep : capture.endpoints) {
|
for (auto &ep : capture.endpoints) {
|
||||||
CellInfo *ci = cell_info(ep.first);
|
CellInfo *ci = cell_info(ep.first);
|
||||||
int clkInfoCount = 0;
|
int clkInfoCount = 0;
|
||||||
@ -1143,7 +1187,7 @@ std::vector<CriticalPath> TimingAnalyser::get_min_delay_violations()
|
|||||||
const auto &launch_clock = launch.key.clock;
|
const auto &launch_clock = launch.key.clock;
|
||||||
|
|
||||||
auto clocks = std::make_pair(launch_clock, capture_clock);
|
auto clocks = std::make_pair(launch_clock, capture_clock);
|
||||||
auto related_clocks = clock_delays.count(clocks) != 0;
|
auto related_clocks = clock_delays.count(clocks) > 0;
|
||||||
|
|
||||||
// Don't consider clocks without known relationships
|
// Don't consider clocks without known relationships
|
||||||
if (launch_id != capture_id && !related_clocks) {
|
if (launch_id != capture_id && !related_clocks) {
|
||||||
@ -1155,9 +1199,13 @@ std::vector<CriticalPath> TimingAnalyser::get_min_delay_violations()
|
|||||||
clock_to_clock = clock_delays.at(clocks);
|
clock_to_clock = clock_delays.at(clocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto hold_slack = arr.value.minDelay() - req.value.maxDelay() + clock_to_clock;
|
auto hold_slack = arr.value.minDelay() + req.value.maxDelay() + clock_to_clock;
|
||||||
|
auto violated = hold_slack <= 0;
|
||||||
|
|
||||||
auto violated = true;
|
// printf("arr min: %f, req max: %f, c2c; %f, slack: %f, arr path len: %d, req path len: %d\n",
|
||||||
|
// ctx->getDelayNS(arr.value.minDelay()), ctx->getDelayNS(req.value.maxDelay()),
|
||||||
|
// ctx->getDelayNS(clock_to_clock), ctx->getDelayNS(hold_slack), arr.path_length,
|
||||||
|
// req.path_length);
|
||||||
|
|
||||||
if (violated) {
|
if (violated) {
|
||||||
const auto dom_pair_id = domain_pair_id(launch_id, capture_id);
|
const auto dom_pair_id = domain_pair_id(launch_id, capture_id);
|
||||||
@ -1233,7 +1281,7 @@ void timing_analysis(Context *ctx, bool print_slack_histogram, bool print_fmax,
|
|||||||
{
|
{
|
||||||
TimingAnalyser tmg(ctx);
|
TimingAnalyser tmg(ctx);
|
||||||
tmg.setup_only = false;
|
tmg.setup_only = false;
|
||||||
tmg.clock_skew = true;
|
tmg.with_clock_skew = false;
|
||||||
tmg.setup(ctx->detailed_timing_report, print_slack_histogram, print_path || print_fmax);
|
tmg.setup(ctx->detailed_timing_report, print_slack_histogram, print_path || print_fmax);
|
||||||
|
|
||||||
auto &result = tmg.get_timing_result();
|
auto &result = tmg.get_timing_result();
|
||||||
|
@ -27,8 +27,8 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
struct CellPortKey
|
struct CellPortKey
|
||||||
{
|
{
|
||||||
CellPortKey() {};
|
CellPortKey(){};
|
||||||
CellPortKey(IdString cell, IdString port) : cell(cell), port(port) {};
|
CellPortKey(IdString cell, IdString port) : cell(cell), port(port){};
|
||||||
explicit CellPortKey(const PortRef &pr)
|
explicit CellPortKey(const PortRef &pr)
|
||||||
{
|
{
|
||||||
NPNR_ASSERT(pr.cell != nullptr);
|
NPNR_ASSERT(pr.cell != nullptr);
|
||||||
@ -49,7 +49,7 @@ struct ClockDomainKey
|
|||||||
{
|
{
|
||||||
IdString clock;
|
IdString clock;
|
||||||
ClockEdge edge;
|
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
|
// probably also need something here to deal with constraints
|
||||||
inline bool is_async() const { return clock == IdString(); }
|
inline bool is_async() const { return clock == IdString(); }
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ typedef int domain_id_t;
|
|||||||
struct ClockDomainPairKey
|
struct ClockDomainPairKey
|
||||||
{
|
{
|
||||||
domain_id_t launch, capture;
|
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
|
inline bool operator==(const ClockDomainPairKey &other) const
|
||||||
{
|
{
|
||||||
return (launch == other.launch) && (capture == other.capture);
|
return (launch == other.launch) && (capture == other.capture);
|
||||||
@ -100,7 +100,7 @@ struct TimingAnalyser
|
|||||||
|
|
||||||
// Enable analysis of clock skew between FFs.
|
// Enable analysis of clock skew between FFs.
|
||||||
// Only do this after legal placement
|
// Only do this after legal placement
|
||||||
bool clock_skew = false;
|
bool with_clock_skew = true;
|
||||||
|
|
||||||
bool setup_only = false;
|
bool setup_only = false;
|
||||||
bool have_loops = false;
|
bool have_loops = false;
|
||||||
@ -180,9 +180,9 @@ struct TimingAnalyser
|
|||||||
ClockEdge edge;
|
ClockEdge edge;
|
||||||
|
|
||||||
CellArc(ArcType type, IdString other_port, DelayQuad value)
|
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)
|
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
|
// Timing data for every cell port
|
||||||
@ -206,7 +206,7 @@ struct TimingAnalyser
|
|||||||
|
|
||||||
struct PerDomain
|
struct PerDomain
|
||||||
{
|
{
|
||||||
PerDomain(ClockDomainKey key) : key(key) {};
|
PerDomain(ClockDomainKey key) : key(key){};
|
||||||
ClockDomainKey key;
|
ClockDomainKey key;
|
||||||
// these are pairs (signal port; clock port)
|
// these are pairs (signal port; clock port)
|
||||||
std::vector<std::pair<CellPortKey, IdString>> startpoints, endpoints;
|
std::vector<std::pair<CellPortKey, IdString>> startpoints, endpoints;
|
||||||
@ -214,7 +214,7 @@ struct TimingAnalyser
|
|||||||
|
|
||||||
struct PerDomainPair
|
struct PerDomainPair
|
||||||
{
|
{
|
||||||
PerDomainPair(ClockDomainPairKey key) : key(key) {};
|
PerDomainPair(ClockDomainPairKey key) : key(key){};
|
||||||
ClockDomainPairKey key;
|
ClockDomainPairKey key;
|
||||||
DelayPair period{0};
|
DelayPair period{0};
|
||||||
delay_t worst_setup_slack, worst_hold_slack;
|
delay_t worst_setup_slack, worst_hold_slack;
|
||||||
|
@ -82,7 +82,7 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
|
|||||||
return ctx->getDelayNS(d.maxDelay());
|
return ctx->getDelayNS(d.maxDelay());
|
||||||
};
|
};
|
||||||
|
|
||||||
log_info("curr total\n");
|
log_info(" curr total type\n");
|
||||||
for (const auto &segment : path.segments) {
|
for (const auto &segment : path.segments) {
|
||||||
|
|
||||||
total += segment.delay;
|
total += segment.delay;
|
||||||
@ -90,14 +90,15 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
|
|||||||
if (segment.type == CriticalPath::Segment::Type::CLK_TO_Q ||
|
if (segment.type == CriticalPath::Segment::Type::CLK_TO_Q ||
|
||||||
segment.type == CriticalPath::Segment::Type::SOURCE ||
|
segment.type == CriticalPath::Segment::Type::SOURCE ||
|
||||||
segment.type == CriticalPath::Segment::Type::LOGIC ||
|
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;
|
logic_total += segment.delay;
|
||||||
|
|
||||||
const std::string type_name = (segment.type == CriticalPath::Segment::Type::SETUP) ? "Setup" : "Source";
|
log_info("% 5.2f % 5.2f %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),
|
||||||
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.second.c_str(ctx));
|
||||||
segment.to.first.c_str(ctx), segment.to.second.c_str(ctx));
|
} else if (segment.type == CriticalPath::Segment::Type::ROUTING ||
|
||||||
} else if (segment.type == CriticalPath::Segment::Type::ROUTING) {
|
segment.type == CriticalPath::Segment::Type::CLK_SKEW) {
|
||||||
route_total = route_total + segment.delay;
|
route_total = route_total + segment.delay;
|
||||||
|
|
||||||
const auto &driver = ctx->cells.at(segment.from.first);
|
const auto &driver = ctx->cells.at(segment.from.first);
|
||||||
@ -106,7 +107,8 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
|
|||||||
auto driver_loc = ctx->getBelLocation(driver->bel);
|
auto driver_loc = ctx->getBelLocation(driver->bel);
|
||||||
auto sink_loc = ctx->getBelLocation(sink->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("% 5.2f % 5.2f %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);
|
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));
|
log_info(" Sink %s.%s\n", segment.to.first.c_str(ctx), segment.to.second.c_str(ctx));
|
||||||
|
|
||||||
@ -145,7 +147,7 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log_info("%.1f ns logic, %.1f ns routing\n", get_delay_ns(logic_total), get_delay_ns(route_total));
|
log_info("%.2f ns logic, %.2f ns routing\n", get_delay_ns(logic_total), get_delay_ns(route_total));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Single domain paths
|
// Single domain paths
|
||||||
@ -175,7 +177,7 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
|
|||||||
auto num_min_violations = result.min_delay_violations.size();
|
auto num_min_violations = result.min_delay_violations.size();
|
||||||
if (num_min_violations > 0) {
|
if (num_min_violations > 0) {
|
||||||
log_break();
|
log_break();
|
||||||
log_info("Hold time violations:\n");
|
log_info("Hold time/min time violation:\n");
|
||||||
for (size_t i = 0; i < std::min((size_t)10, num_min_violations); ++i) {
|
for (size_t i = 0; i < std::min((size_t)10, num_min_violations); ++i) {
|
||||||
auto &report = result.min_delay_violations.at(i);
|
auto &report = result.min_delay_violations.at(i);
|
||||||
log_break();
|
log_break();
|
||||||
|
Loading…
Reference in New Issue
Block a user