Merge 0e848d4fe8
into 1967db170d
This commit is contained in:
commit
eec9a80574
@ -34,6 +34,7 @@
|
||||
#include "nextpnr_types.h"
|
||||
#include "property.h"
|
||||
#include "str_ring_buffer.h"
|
||||
#include "timing_constraint.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
@ -84,6 +85,9 @@ struct BaseCtx
|
||||
// Context meta data
|
||||
dict<IdString, Property> attrs;
|
||||
|
||||
// Path constraints set via SDC
|
||||
std::vector<PathConstraint> path_constraints;
|
||||
|
||||
// Fmax data post timing analysis
|
||||
TimingResult timing_result;
|
||||
|
||||
|
@ -29,11 +29,27 @@
|
||||
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include "hashlib.h"
|
||||
#include "idstring.h"
|
||||
#include "nextpnr_namespaces.h"
|
||||
|
||||
/* Helper struct to overload lambdas for variabt visiting
|
||||
so you can do:
|
||||
std::variant<int, std::string> var = 42;
|
||||
|
||||
std::visit(overloaded{
|
||||
[](int arg) { std::cout << "Integer: " << arg << '\n'; },
|
||||
[](const std::string& arg) { std::cout << "String: " << arg << '\n'; }
|
||||
}, var);
|
||||
*/
|
||||
template <class... Ts> struct overloaded : Ts...
|
||||
{
|
||||
using Ts::operator()...;
|
||||
};
|
||||
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct GraphicElement
|
||||
|
@ -369,6 +369,27 @@ struct CellInfo : ArchCellInfo
|
||||
int new_offset, bool new_brackets, int width);
|
||||
};
|
||||
|
||||
// similar to PortRef but allows storage into pool and dict
|
||||
struct CellPortKey
|
||||
{
|
||||
CellPortKey(){};
|
||||
CellPortKey(IdString cell, IdString port) : cell(cell), port(port){};
|
||||
explicit CellPortKey(const PortRef &pr)
|
||||
{
|
||||
NPNR_ASSERT(pr.cell != nullptr);
|
||||
cell = pr.cell->name;
|
||||
port = pr.port;
|
||||
}
|
||||
IdString cell, port;
|
||||
unsigned int hash() const { return mkhash(cell.hash(), port.hash()); }
|
||||
inline bool operator==(const CellPortKey &other) const { return (cell == other.cell) && (port == other.port); }
|
||||
inline bool operator!=(const CellPortKey &other) const { return (cell != other.cell) || (port != other.port); }
|
||||
inline bool operator<(const CellPortKey &other) const
|
||||
{
|
||||
return cell == other.cell ? port < other.port : cell < other.cell;
|
||||
}
|
||||
};
|
||||
|
||||
struct ClockConstraint
|
||||
{
|
||||
DelayPair high;
|
||||
|
@ -21,12 +21,29 @@
|
||||
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "timing_constraint.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
bool is_startpoint(Context *ctx, const PortRef &port)
|
||||
{
|
||||
int clkInfoCount;
|
||||
TimingPortClass cls = ctx->getPortTimingClass(port.cell, port.port, clkInfoCount);
|
||||
|
||||
return cls == TMG_STARTPOINT || cls == TMG_REGISTER_OUTPUT;
|
||||
}
|
||||
|
||||
bool is_endpoint(Context *ctx, const PortRef &port)
|
||||
{
|
||||
int clkInfoCount;
|
||||
TimingPortClass cls = ctx->getPortTimingClass(port.cell, port.port, clkInfoCount);
|
||||
|
||||
return cls == TMG_ENDPOINT || cls == TMG_REGISTER_OUTPUT;
|
||||
}
|
||||
|
||||
struct SdcEntity
|
||||
{
|
||||
enum EntityType
|
||||
@ -58,6 +75,23 @@ struct SdcEntity
|
||||
return &ctx->ports.at(name);
|
||||
}
|
||||
|
||||
PortRef get_pin(Context *ctx) const
|
||||
{
|
||||
if (type != ENTITY_PIN)
|
||||
return PortRef{nullptr, IdString()};
|
||||
|
||||
CellInfo *cell = nullptr;
|
||||
if (ctx->cells.count(name)) {
|
||||
cell = ctx->cells.at(name).get();
|
||||
} else {
|
||||
return PortRef{nullptr, IdString()};
|
||||
}
|
||||
if (!cell->ports.count(pin))
|
||||
return PortRef{nullptr, IdString()};
|
||||
|
||||
return PortRef{cell, pin};
|
||||
}
|
||||
|
||||
NetInfo *get_net(Context *ctx) const
|
||||
{
|
||||
if (type == ENTITY_PIN) {
|
||||
@ -80,8 +114,8 @@ struct SdcEntity
|
||||
|
||||
struct SdcValue
|
||||
{
|
||||
SdcValue(const std::string &s) : is_string(true), str(s) {};
|
||||
SdcValue(const std::vector<SdcEntity> &l) : is_string(false), list(l) {};
|
||||
SdcValue(const std::string &s) : is_string(true), str(s){};
|
||||
SdcValue(const std::vector<SdcEntity> &l) : is_string(false), list(l){};
|
||||
|
||||
bool is_string;
|
||||
std::string str; // simple string value
|
||||
@ -95,7 +129,7 @@ struct SDCParser
|
||||
int lineno = 1;
|
||||
Context *ctx;
|
||||
|
||||
SDCParser(const std::string &buf, Context *ctx) : buf(buf), ctx(ctx) {};
|
||||
SDCParser(const std::string &buf, Context *ctx) : buf(buf), ctx(ctx){};
|
||||
|
||||
inline bool eof() const { return pos == int(buf.size()); }
|
||||
|
||||
@ -250,6 +284,60 @@ struct SDCParser
|
||||
return args;
|
||||
}
|
||||
|
||||
// Parse an argument to -from/to into the path_constraint
|
||||
void sdc_into_path_constraint(const SdcEntity &ety, bool is_from, PathConstraint &ct)
|
||||
{
|
||||
auto &target = is_from ? ct.from : ct.to;
|
||||
auto test_port = is_from ? is_startpoint : is_endpoint;
|
||||
std::string tartget_str = is_from ? "startpoint" : "endpoint";
|
||||
|
||||
if (ety.type == SdcEntity::ENTITY_PIN) {
|
||||
auto port_ref = ety.get_pin(ctx);
|
||||
if (test_port(ctx, port_ref) == false) {
|
||||
log_error("\"%s.%s\" is not a timing %s (line %d)\n", port_ref.cell->name.c_str(ctx),
|
||||
port_ref.port.c_str(ctx), tartget_str.c_str(), lineno);
|
||||
}
|
||||
target.emplace(CellPortKey(port_ref));
|
||||
} else if (ety.type == SdcEntity::ENTITY_NET) {
|
||||
auto net = ety.get_net(ctx);
|
||||
if (is_from) {
|
||||
auto port_ref = net->driver;
|
||||
if (test_port(ctx, port_ref) == false) {
|
||||
log_error("\"%s.%s\" is not a timing %s (line %d)\n", port_ref.cell->name.c_str(ctx),
|
||||
port_ref.port.c_str(ctx), tartget_str.c_str(), lineno);
|
||||
}
|
||||
target.emplace(CellPortKey(port_ref));
|
||||
} else {
|
||||
for (const auto &usr : net->users) {
|
||||
if (test_port(ctx, usr) == false) {
|
||||
log_error("\"%s.%s\" is not a timing %s (line %d)\n", usr.cell->name.c_str(ctx),
|
||||
usr.port.c_str(ctx), tartget_str.c_str(), lineno);
|
||||
}
|
||||
target.emplace(CellPortKey(usr));
|
||||
}
|
||||
}
|
||||
} else if (ety.type == SdcEntity::ENTITY_PORT) {
|
||||
auto ioport_ref = ety.get_port(ctx);
|
||||
auto net = ioport_ref->net;
|
||||
if (is_from) {
|
||||
auto port_ref = net->driver;
|
||||
if (test_port(ctx, port_ref) == false) {
|
||||
log_error("\"%s.%s\" is not a timing %s (line %d)\n", port_ref.cell->name.c_str(ctx),
|
||||
port_ref.port.c_str(ctx), tartget_str.c_str(), lineno);
|
||||
}
|
||||
target.emplace(CellPortKey(port_ref));
|
||||
} else {
|
||||
for (const auto &usr : net->users) {
|
||||
if (test_port(ctx, usr) == false) {
|
||||
log_error("\"%s.%s\" is not a timing %s (line %d)\n", usr.cell->name.c_str(ctx),
|
||||
usr.port.c_str(ctx), tartget_str.c_str(), lineno);
|
||||
}
|
||||
target.emplace(CellPortKey(usr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SdcValue cmd_get_nets(const std::vector<SdcValue> &arguments)
|
||||
{
|
||||
std::vector<SdcEntity> nets;
|
||||
@ -317,7 +405,7 @@ struct SDCParser
|
||||
if (pos == std::string::npos)
|
||||
log_error("expected / in cell pin name '%s' (line %d)\n", s.c_str(), lineno);
|
||||
pins.emplace_back(SdcEntity::ENTITY_PIN, ctx->id(s.substr(0, pos)), ctx->id(s.substr(pos + 1)));
|
||||
if (pins.back().get_net(ctx) == nullptr) {
|
||||
if (pins.back().get_pin(ctx).cell == nullptr) {
|
||||
log_warning("cell pin '%s' not found\n", s.c_str());
|
||||
pins.pop_back();
|
||||
}
|
||||
@ -368,8 +456,8 @@ struct SDCParser
|
||||
|
||||
SdcValue cmd_set_false_path(const std::vector<SdcValue> &arguments)
|
||||
{
|
||||
NetInfo *from = nullptr;
|
||||
NetInfo *to = nullptr;
|
||||
PathConstraint ct;
|
||||
ct.exception = FalsePath{};
|
||||
|
||||
for (int i = 1; i < int(arguments.size()); i++) {
|
||||
auto &arg = arguments.at(i);
|
||||
@ -389,38 +477,19 @@ struct SDCParser
|
||||
log_error("expecting SdcValue argument to -from (line %d)\n", lineno);
|
||||
}
|
||||
|
||||
if (val.list.size() != 1) {
|
||||
log_error("Expected a single SdcEntity as argument to -to/-from (line %d)\n", lineno);
|
||||
}
|
||||
|
||||
auto &ety = val.list.at(0);
|
||||
|
||||
NetInfo *net = nullptr;
|
||||
if (ety.type == SdcEntity::ENTITY_PIN)
|
||||
net = ety.get_net(ctx);
|
||||
else if (ety.type == SdcEntity::ENTITY_NET)
|
||||
net = ctx->nets.at(ety.name).get();
|
||||
else if (ety.type == SdcEntity::ENTITY_PORT)
|
||||
net = ctx->ports.at(ety.name).net;
|
||||
else
|
||||
log_error("set_false_path applies only to nets, cell pins, or IO ports (line %d)\n", lineno);
|
||||
|
||||
if (is_from) {
|
||||
from = net;
|
||||
} else {
|
||||
to = net;
|
||||
for (const auto &ety : val.list) {
|
||||
sdc_into_path_constraint(ety, is_from, ct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (from == nullptr) {
|
||||
log_error("-from is required for set_false_path (line %d)\n", lineno);
|
||||
} else if (to == nullptr) {
|
||||
log_error("-to is required for set_false_path (line %d)\n", lineno);
|
||||
if (ct.from.empty()) {
|
||||
log_error("query specified in -from did not find any pins or ports (line %d)\n", lineno);
|
||||
} else if (ct.to.empty()) {
|
||||
log_error("query specified in -to did not find any pins or ports (line %d)\n", lineno);
|
||||
}
|
||||
|
||||
log_warning("set_false_path from: %s, to: %s does not do anything(yet).\n", from->name.c_str(ctx),
|
||||
to->name.c_str(ctx));
|
||||
ctx->path_constraints.emplace_back(ct);
|
||||
|
||||
return std::string{};
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ void TimingAnalyser::setup(bool update_net_timings, bool update_histogram, bool
|
||||
init_ports();
|
||||
get_cell_delays();
|
||||
topo_sort();
|
||||
setup_port_domains();
|
||||
setup_port_domains_and_constraints();
|
||||
identify_related_domains();
|
||||
run(true, update_net_timings, update_histogram, update_crit_paths);
|
||||
}
|
||||
@ -229,7 +229,7 @@ void TimingAnalyser::topo_sort()
|
||||
std::swap(topological_order, topo.sorted);
|
||||
}
|
||||
|
||||
void TimingAnalyser::setup_port_domains()
|
||||
void TimingAnalyser::setup_port_domains_and_constraints()
|
||||
{
|
||||
for (auto &d : domains) {
|
||||
d.startpoints.clear();
|
||||
@ -238,7 +238,7 @@ void TimingAnalyser::setup_port_domains()
|
||||
bool first_iter = true;
|
||||
do {
|
||||
// Go forward through the topological order (domains from the PoV of arrival time)
|
||||
updated_domains = false;
|
||||
updated_domains_constraints = false;
|
||||
for (auto port : topological_order) {
|
||||
auto &pd = ports.at(port);
|
||||
auto &pi = port_info(port);
|
||||
@ -256,18 +256,19 @@ void TimingAnalyser::setup_port_domains()
|
||||
// create per-domain data
|
||||
pd.arrival[dom];
|
||||
domains.at(dom).startpoints.emplace_back(port, fanin.other_port);
|
||||
// TODO: add all constraints on this startpoint
|
||||
}
|
||||
}
|
||||
// copy domains across routing
|
||||
if (pi.net != nullptr)
|
||||
for (auto &usr : pi.net->users)
|
||||
copy_domains(port, CellPortKey(usr), false);
|
||||
propagate_domains_and_constraints(port, CellPortKey(usr), false);
|
||||
} else {
|
||||
// copy domains from input to output
|
||||
for (auto &fanout : pd.cell_arcs) {
|
||||
if (fanout.type != CellArc::COMBINATIONAL)
|
||||
continue;
|
||||
copy_domains(port, CellPortKey(port.cell, fanout.other_port), false);
|
||||
propagate_domains_and_constraints(port, CellPortKey(port.cell, fanout.other_port), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -280,7 +281,7 @@ void TimingAnalyser::setup_port_domains()
|
||||
for (auto &fanin : pd.cell_arcs) {
|
||||
if (fanin.type != CellArc::COMBINATIONAL)
|
||||
continue;
|
||||
copy_domains(port, CellPortKey(port.cell, fanin.other_port), true);
|
||||
propagate_domains_and_constraints(port, CellPortKey(port.cell, fanin.other_port), true);
|
||||
}
|
||||
} else {
|
||||
if (first_iter) {
|
||||
@ -296,11 +297,12 @@ void TimingAnalyser::setup_port_domains()
|
||||
// create per-domain data
|
||||
pd.required[dom];
|
||||
domains.at(dom).endpoints.emplace_back(port, fanout.other_port);
|
||||
// TODO: add all constraints on this endpoint
|
||||
}
|
||||
}
|
||||
// copy port to driver
|
||||
if (pi.net != nullptr && pi.net->driver.cell != nullptr)
|
||||
copy_domains(port, CellPortKey(pi.net->driver), true);
|
||||
propagate_domains_and_constraints(port, CellPortKey(pi.net->driver), true);
|
||||
}
|
||||
}
|
||||
// Iterate over ports and find domain pairs
|
||||
@ -314,7 +316,7 @@ void TimingAnalyser::setup_port_domains()
|
||||
first_iter = false;
|
||||
// If there are loops, repeat the process until a fixed point is reached, as there might be unusual ways to
|
||||
// visit points, which would result in a missing domain key and therefore crash later on
|
||||
} while (have_loops && updated_domains);
|
||||
} while (have_loops && updated_domains_constraints);
|
||||
for (auto &dp : domain_pairs) {
|
||||
auto &launch_data = domains.at(dp.key.launch);
|
||||
auto &capture_data = domains.at(dp.key.capture);
|
||||
@ -1294,12 +1296,25 @@ domain_id_t TimingAnalyser::domain_pair_id(domain_id_t launch, domain_id_t captu
|
||||
return inserted.first->second;
|
||||
}
|
||||
|
||||
void TimingAnalyser::copy_domains(const CellPortKey &from, const CellPortKey &to, bool backward)
|
||||
void TimingAnalyser::propagate_domains_and_constraints(const CellPortKey &from, const CellPortKey &to, bool backward)
|
||||
{
|
||||
auto &f = ports.at(from), &t = ports.at(to);
|
||||
for (auto &dom : (backward ? f.required : f.arrival)) {
|
||||
updated_domains |= (backward ? t.required : t.arrival).emplace(dom.first, ArrivReqTime{}).second;
|
||||
updated_domains_constraints |= (backward ? t.required : t.arrival).emplace(dom.first, ArrivReqTime{}).second;
|
||||
}
|
||||
|
||||
// for (auto &ct : f.per_constraint) {
|
||||
// bool has_constraint = t.per_constraint.count(ct.first) > 0;
|
||||
// bool same_constraint = has_constraint ? ct.second == t.per_constraint.at(ct.first) : false;
|
||||
|
||||
// if (t.per_constraint.count(ct.first) > 0) {
|
||||
// if (backward) {
|
||||
// t.per_constraint[ct.first] = CONSTRAINED;
|
||||
// }
|
||||
// } else if (!backward) {
|
||||
// t.per_constraint[ct.first] = FORWARDONLY;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
const std::string TimingAnalyser::arcType_to_str(CellArc::ArcType typ)
|
||||
|
@ -25,26 +25,6 @@
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct CellPortKey
|
||||
{
|
||||
CellPortKey(){};
|
||||
CellPortKey(IdString cell, IdString port) : cell(cell), port(port){};
|
||||
explicit CellPortKey(const PortRef &pr)
|
||||
{
|
||||
NPNR_ASSERT(pr.cell != nullptr);
|
||||
cell = pr.cell->name;
|
||||
port = pr.port;
|
||||
}
|
||||
IdString cell, port;
|
||||
unsigned int hash() const { return mkhash(cell.hash(), port.hash()); }
|
||||
inline bool operator==(const CellPortKey &other) const { return (cell == other.cell) && (port == other.port); }
|
||||
inline bool operator!=(const CellPortKey &other) const { return (cell != other.cell) || (port != other.port); }
|
||||
inline bool operator<(const CellPortKey &other) const
|
||||
{
|
||||
return cell == other.cell ? port < other.port : cell < other.cell;
|
||||
}
|
||||
};
|
||||
|
||||
struct ClockDomainKey
|
||||
{
|
||||
IdString clock;
|
||||
@ -58,6 +38,8 @@ struct ClockDomainKey
|
||||
inline bool operator==(const ClockDomainKey &other) const { return (clock == other.clock) && (edge == other.edge); }
|
||||
};
|
||||
|
||||
typedef int exception_id_t;
|
||||
|
||||
typedef int domain_id_t;
|
||||
|
||||
struct ClockDomainPairKey
|
||||
@ -103,14 +85,14 @@ struct TimingAnalyser
|
||||
|
||||
bool setup_only = false;
|
||||
bool have_loops = false;
|
||||
bool updated_domains = false;
|
||||
bool updated_domains_constraints = false;
|
||||
|
||||
private:
|
||||
void init_ports();
|
||||
void get_cell_delays();
|
||||
void get_route_delays();
|
||||
void topo_sort();
|
||||
void setup_port_domains();
|
||||
void setup_port_domains_and_constraints();
|
||||
void identify_related_domains();
|
||||
|
||||
void reset_times();
|
||||
@ -188,6 +170,18 @@ struct TimingAnalyser
|
||||
: type(type), other_port(other_port), value(value), edge(edge){};
|
||||
};
|
||||
|
||||
// To track whether a path has a timing exception during a forwards/backwards pass.
|
||||
// During the forward pass the startpoints propagate out FORWARDONLY.
|
||||
// During the backwards pass all ports that contain a "FORWARDONLY" will
|
||||
// move to "CONSTRAINED". Once the forward and backward passes have been
|
||||
// done only the constraints on ports that are "CONSTRAINED" apply.
|
||||
enum class HasPathException
|
||||
{
|
||||
FORWARDONLY,
|
||||
BACKWARDONLY,
|
||||
CONSTRAINED
|
||||
};
|
||||
|
||||
// Timing data for every cell port
|
||||
struct PerPort
|
||||
{
|
||||
@ -205,6 +199,9 @@ struct TimingAnalyser
|
||||
float worst_crit = 0;
|
||||
delay_t worst_setup_slack = std::numeric_limits<delay_t>::max(),
|
||||
worst_hold_slack = std::numeric_limits<delay_t>::max();
|
||||
// Forall timing constraints the uint8_t indicates
|
||||
// - During forward walking
|
||||
dict<exception_id_t, uint8_t> per_timing_exception;
|
||||
};
|
||||
|
||||
struct PerDomain
|
||||
@ -230,7 +227,7 @@ struct TimingAnalyser
|
||||
domain_id_t domain_id(const NetInfo *net, ClockEdge edge);
|
||||
domain_id_t domain_pair_id(domain_id_t launch, domain_id_t capture);
|
||||
|
||||
void copy_domains(const CellPortKey &from, const CellPortKey &to, bool backwards);
|
||||
void propagate_domains_and_constraints(const CellPortKey &from, const CellPortKey &to, bool backwards);
|
||||
|
||||
[[maybe_unused]] static const std::string arcType_to_str(CellArc::ArcType typ);
|
||||
|
||||
@ -240,10 +237,12 @@ struct TimingAnalyser
|
||||
std::vector<PerDomain> domains;
|
||||
std::vector<PerDomainPair> domain_pairs;
|
||||
dict<std::pair<IdString, IdString>, delay_t> clock_delays;
|
||||
// std::vector<PathConstraint> path_constraints;
|
||||
|
||||
std::vector<CellPortKey> topological_order;
|
||||
|
||||
domain_id_t async_clock_id;
|
||||
exception_id_t no_exception_id;
|
||||
|
||||
Context *ctx;
|
||||
|
||||
|
37
common/kernel/timing_constraint.cc
Normal file
37
common/kernel/timing_constraint.cc
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 rowanG077 <goemansrowan@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "timing_constraint.h"
|
||||
#include "log.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
const std::string MinMaxDelay::type_to_str(MinMaxDelay::Type typ)
|
||||
{
|
||||
switch (typ) {
|
||||
case Type::MAXDELAY:
|
||||
return "MAXDELAY";
|
||||
case Type::MINDELAY:
|
||||
return "MINDELAY";
|
||||
default:
|
||||
log_error("Impossible MinMaxDelay::Type");
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
68
common/kernel/timing_constraint.h
Normal file
68
common/kernel/timing_constraint.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 rowanG077 <goemansrowan@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TIMING_CONSTRAINT_H
|
||||
#define TIMING_CONSTRAINT_H
|
||||
|
||||
#include "nextpnr_types.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct FalsePath
|
||||
{
|
||||
};
|
||||
|
||||
struct MinMaxDelay
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
MAXDELAY,
|
||||
MINDELAY
|
||||
};
|
||||
|
||||
[[maybe_unused]] const std::string type_to_str(Type typ);
|
||||
|
||||
Type type;
|
||||
delay_t delay;
|
||||
bool datapath_only;
|
||||
};
|
||||
|
||||
struct MultiCycle
|
||||
{
|
||||
size_t cycles;
|
||||
enum class Type
|
||||
{
|
||||
SETUP,
|
||||
HOLD
|
||||
};
|
||||
};
|
||||
|
||||
using TimingException = std::variant<FalsePath, MinMaxDelay, MultiCycle>;
|
||||
|
||||
struct PathConstraint
|
||||
{
|
||||
TimingException exception;
|
||||
|
||||
pool<CellPortKey> from;
|
||||
pool<CellPortKey> to;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user