From 334ac9d13a8152a85837456e1cdbe04671cbc115 Mon Sep 17 00:00:00 2001 From: Rowan Goemans Date: Mon, 23 Sep 2024 12:22:57 +0200 Subject: [PATCH 1/9] timing: Start TimingException implementation --- common/kernel/timing.cc | 29 ++++++++++++++++---- common/kernel/timing.h | 61 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/common/kernel/timing.cc b/common/kernel/timing.cc index 880cc50691..2cc7cd63ac 100644 --- a/common/kernel/timing.cc +++ b/common/kernel/timing.cc @@ -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,6 +256,7 @@ 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 @@ -296,6 +297,7 @@ 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 @@ -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); @@ -1298,7 +1300,24 @@ void TimingAnalyser::copy_domains(const CellPortKey &from, const CellPortKey &to { 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; + } +} + +void TimingAnalyser::propagate_constraints(const CellPortKey &from, const CellPortKey &to, bool backward) +{ + auto &f = ports.at(from), &t = ports.at(to); + 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; + } } } diff --git a/common/kernel/timing.h b/common/kernel/timing.h index f82a1b6da6..1915f9ec23 100644 --- a/common/kernel/timing.h +++ b/common/kernel/timing.h @@ -58,6 +58,49 @@ struct ClockDomainKey inline bool operator==(const ClockDomainKey &other) const { return (clock == other.clock) && (edge == other.edge); } }; +// TODO: perhaps move these elsewhere +struct FalsePath +{ +}; + +struct MinMaxDelay +{ + enum class Type + { + MAXDELAY, + MINDELAY + }; + + [[maybe_unused]] static const std::string type_to_str(Type typ) + { + switch (typ) { + case Type::MAXDELAY: + return "MAXDELAY"; + case Type::MINDELAY: + return "MINDELAY"; + default: + log_error("Impossible MinMaxDelay::Type"); + } + } + + Type type; + delay_t delay; + bool datapath_only; +}; + +struct MultiCycle +{ + size_t cycles; +}; + +struct TimingException +{ + std::variant type; + + pool startpoints; + pool endpoints; +}; + typedef int domain_id_t; struct ClockDomainPairKey @@ -71,6 +114,8 @@ struct ClockDomainPairKey unsigned int hash() const { return mkhash(launch, capture); } }; +typedef int constraint_id_t; + struct TimingAnalyser { public: @@ -103,14 +148,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 +233,12 @@ struct TimingAnalyser : type(type), other_port(other_port), value(value), edge(edge){}; }; + enum HasConstraint + { + FORWARDONLY, + CONSTRAINED + }; + // Timing data for every cell port struct PerPort { @@ -205,6 +256,9 @@ struct TimingAnalyser float worst_crit = 0; delay_t worst_setup_slack = std::numeric_limits::max(), worst_hold_slack = std::numeric_limits::max(); + // Forall timing constraints the uint8_t indicates + // - During forward walking + dict per_constraint; }; struct PerDomain @@ -232,6 +286,8 @@ struct TimingAnalyser void copy_domains(const CellPortKey &from, const CellPortKey &to, bool backwards); + void propagate_constraints(const CellPortKey &from, const CellPortKey &to, bool backwards); + [[maybe_unused]] static const std::string arcType_to_str(CellArc::ArcType typ); dict ports; @@ -244,6 +300,7 @@ struct TimingAnalyser std::vector topological_order; domain_id_t async_clock_id; + constraint_id_t clock_constraint_id; Context *ctx; From 92a92abd567d3d5b97147bc98e2317cc042d6796 Mon Sep 17 00:00:00 2001 From: Rowan Goemans Date: Mon, 23 Sep 2024 16:12:23 +0200 Subject: [PATCH 2/9] common: Move CellPortKey to nextpnr_types.h --- common/kernel/nextpnr_types.h | 21 +++++++++ common/kernel/timing.cc | 38 ++++++++-------- common/kernel/timing.h | 82 +++++------------------------------ 3 files changed, 50 insertions(+), 91 deletions(-) diff --git a/common/kernel/nextpnr_types.h b/common/kernel/nextpnr_types.h index e23cd16a35..3abdb81fb1 100644 --- a/common/kernel/nextpnr_types.h +++ b/common/kernel/nextpnr_types.h @@ -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; diff --git a/common/kernel/timing.cc b/common/kernel/timing.cc index 2cc7cd63ac..9d37026363 100644 --- a/common/kernel/timing.cc +++ b/common/kernel/timing.cc @@ -262,13 +262,13 @@ void TimingAnalyser::setup_port_domains_and_constraints() // 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); } } } @@ -281,7 +281,7 @@ void TimingAnalyser::setup_port_domains_and_constraints() 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) { @@ -302,7 +302,7 @@ void TimingAnalyser::setup_port_domains_and_constraints() } // 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 @@ -1296,29 +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_constraints |= (backward ? t.required : t.arrival).emplace(dom.first, ArrivReqTime{}).second; } -} - -void TimingAnalyser::propagate_constraints(const CellPortKey &from, const CellPortKey &to, bool backward) -{ - auto &f = ports.at(from), &t = ports.at(to); - 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; - } - } + // 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) diff --git a/common/kernel/timing.h b/common/kernel/timing.h index 1915f9ec23..c1f4469cd3 100644 --- a/common/kernel/timing.h +++ b/common/kernel/timing.h @@ -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,48 +38,7 @@ struct ClockDomainKey inline bool operator==(const ClockDomainKey &other) const { return (clock == other.clock) && (edge == other.edge); } }; -// TODO: perhaps move these elsewhere -struct FalsePath -{ -}; - -struct MinMaxDelay -{ - enum class Type - { - MAXDELAY, - MINDELAY - }; - - [[maybe_unused]] static const std::string type_to_str(Type typ) - { - switch (typ) { - case Type::MAXDELAY: - return "MAXDELAY"; - case Type::MINDELAY: - return "MINDELAY"; - default: - log_error("Impossible MinMaxDelay::Type"); - } - } - - Type type; - delay_t delay; - bool datapath_only; -}; - -struct MultiCycle -{ - size_t cycles; -}; - -struct TimingException -{ - std::variant type; - - pool startpoints; - pool endpoints; -}; +typedef int exception_id_t; typedef int domain_id_t; @@ -114,8 +53,6 @@ struct ClockDomainPairKey unsigned int hash() const { return mkhash(launch, capture); } }; -typedef int constraint_id_t; - struct TimingAnalyser { public: @@ -233,9 +170,15 @@ struct TimingAnalyser : type(type), other_port(other_port), value(value), edge(edge){}; }; - enum HasConstraint + // 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 }; @@ -258,7 +201,7 @@ struct TimingAnalyser worst_hold_slack = std::numeric_limits::max(); // Forall timing constraints the uint8_t indicates // - During forward walking - dict per_constraint; + dict per_timing_exception; }; struct PerDomain @@ -284,9 +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_constraints(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); @@ -296,11 +237,12 @@ struct TimingAnalyser std::vector domains; std::vector domain_pairs; dict, delay_t> clock_delays; + // std::vector path_constraints; std::vector topological_order; domain_id_t async_clock_id; - constraint_id_t clock_constraint_id; + exception_id_t no_exception_id; Context *ctx; From ced9703fd3c1a885eceff453057736d1f278bf52 Mon Sep 17 00:00:00 2001 From: Rowan Goemans Date: Tue, 24 Sep 2024 18:12:01 +0200 Subject: [PATCH 3/9] Add timing_constraint.h --- common/kernel/sdc.cc | 7 +-- common/kernel/timing_contraint.h | 73 ++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 common/kernel/timing_contraint.h diff --git a/common/kernel/sdc.cc b/common/kernel/sdc.cc index 0fbfe69009..214acc2fe6 100644 --- a/common/kernel/sdc.cc +++ b/common/kernel/sdc.cc @@ -21,6 +21,7 @@ #include "log.h" #include "nextpnr.h" +#include "timing_constraint.h" #include #include @@ -80,8 +81,8 @@ struct SdcEntity struct SdcValue { - SdcValue(const std::string &s) : is_string(true), str(s) {}; - SdcValue(const std::vector &l) : is_string(false), list(l) {}; + SdcValue(const std::string &s) : is_string(true), str(s){}; + SdcValue(const std::vector &l) : is_string(false), list(l){}; bool is_string; std::string str; // simple string value @@ -95,7 +96,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()); } diff --git a/common/kernel/timing_contraint.h b/common/kernel/timing_contraint.h new file mode 100644 index 0000000000..e7e697eb21 --- /dev/null +++ b/common/kernel/timing_contraint.h @@ -0,0 +1,73 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 rowanG077 + * + * 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.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct FalsePath +{ +}; + +struct MinMaxDelay +{ + enum class Type + { + MAXDELAY, + MINDELAY + }; + + [[maybe_unused]] static const std::string type_to_str(Type typ) + { + switch (typ) { + case Type::MAXDELAY: + return "MAXDELAY"; + case Type::MINDELAY: + return "MINDELAY"; + default: + log_error("Impossible MinMaxDelay::Type"); + } + } + + Type type; + delay_t delay; + bool datapath_only; +}; + +struct MultiCycle +{ + size_t cycles; +}; + +using TimingException = std::variant; + +struct PathConstraint +{ + TimingException exception; + + pool from; + pool to; +}; + +NEXTPNR_NAMESPACE_END + +#endif From 027cf94fbafc3594206119ebe00d28b2a5593efb Mon Sep 17 00:00:00 2001 From: Rowan Goemans Date: Thu, 26 Sep 2024 18:24:16 +0200 Subject: [PATCH 4/9] timing_constraint: add file --- common/kernel/timing_constraint.cc | 37 ++++++++++++++++ common/kernel/timing_constraint.h | 68 ++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 common/kernel/timing_constraint.cc create mode 100644 common/kernel/timing_constraint.h diff --git a/common/kernel/timing_constraint.cc b/common/kernel/timing_constraint.cc new file mode 100644 index 0000000000..20b34b09e1 --- /dev/null +++ b/common/kernel/timing_constraint.cc @@ -0,0 +1,37 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 rowanG077 + * + * 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 diff --git a/common/kernel/timing_constraint.h b/common/kernel/timing_constraint.h new file mode 100644 index 0000000000..69a2f91007 --- /dev/null +++ b/common/kernel/timing_constraint.h @@ -0,0 +1,68 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2024 rowanG077 + * + * 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; + +struct PathConstraint +{ + TimingException exception; + + pool from; + pool to; +}; + +NEXTPNR_NAMESPACE_END + +#endif From be826df3ce838e11ce45ae307d12e17e7804e51f Mon Sep 17 00:00:00 2001 From: Rowan Goemans Date: Thu, 26 Sep 2024 18:24:34 +0200 Subject: [PATCH 5/9] basectx: Store path constraints into the context for now --- common/kernel/basectx.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/kernel/basectx.h b/common/kernel/basectx.h index c8791a2b96..0b8acf0ddd 100644 --- a/common/kernel/basectx.h +++ b/common/kernel/basectx.h @@ -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 attrs; + // Path constraints set via SDC + std::vector path_constraints; + // Fmax data post timing analysis TimingResult timing_result; From 0729f0166c0dd6b4d4ed7697238406b2f8fd8a7b Mon Sep 17 00:00:00 2001 From: Rowan Goemans Date: Thu, 26 Sep 2024 18:24:59 +0200 Subject: [PATCH 6/9] nextpnr_base_types: Add overload lambda visitor helper --- common/kernel/nextpnr_base_types.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/common/kernel/nextpnr_base_types.h b/common/kernel/nextpnr_base_types.h index 80a0e24ee1..4f0d37fb1f 100644 --- a/common/kernel/nextpnr_base_types.h +++ b/common/kernel/nextpnr_base_types.h @@ -29,11 +29,27 @@ #include #include +#include #include "hashlib.h" #include "idstring.h" #include "nextpnr_namespaces.h" +/* Helper struct to overload lambdas for variabt visiting + so you can do: + std::variant var = 42; + + std::visit(overloaded{ + [](int arg) { std::cout << "Integer: " << arg << '\n'; }, + [](const std::string& arg) { std::cout << "String: " << arg << '\n'; } + }, var); +*/ +template struct overloaded : Ts... +{ + using Ts::operator()...; +}; +template overloaded(Ts...) -> overloaded; + NEXTPNR_NAMESPACE_BEGIN struct GraphicElement From 7f35139b8e128011e7fa17c4290403fad622f981 Mon Sep 17 00:00:00 2001 From: Rowan Goemans Date: Thu, 26 Sep 2024 18:25:27 +0200 Subject: [PATCH 7/9] timing_constraint: Delete stale file --- common/kernel/timing_contraint.h | 73 -------------------------------- 1 file changed, 73 deletions(-) delete mode 100644 common/kernel/timing_contraint.h diff --git a/common/kernel/timing_contraint.h b/common/kernel/timing_contraint.h deleted file mode 100644 index e7e697eb21..0000000000 --- a/common/kernel/timing_contraint.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2024 rowanG077 - * - * 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.h" - -NEXTPNR_NAMESPACE_BEGIN - -struct FalsePath -{ -}; - -struct MinMaxDelay -{ - enum class Type - { - MAXDELAY, - MINDELAY - }; - - [[maybe_unused]] static const std::string type_to_str(Type typ) - { - switch (typ) { - case Type::MAXDELAY: - return "MAXDELAY"; - case Type::MINDELAY: - return "MINDELAY"; - default: - log_error("Impossible MinMaxDelay::Type"); - } - } - - Type type; - delay_t delay; - bool datapath_only; -}; - -struct MultiCycle -{ - size_t cycles; -}; - -using TimingException = std::variant; - -struct PathConstraint -{ - TimingException exception; - - pool from; - pool to; -}; - -NEXTPNR_NAMESPACE_END - -#endif From e7f8ca5ea627680fa90947010b91779e1c686aec Mon Sep 17 00:00:00 2001 From: Rowan Goemans Date: Thu, 26 Sep 2024 18:26:03 +0200 Subject: [PATCH 8/9] sdc: add_false_path to PathConstraint logic --- common/kernel/sdc.cc | 120 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 24 deletions(-) diff --git a/common/kernel/sdc.cc b/common/kernel/sdc.cc index 214acc2fe6..bc93da9b10 100644 --- a/common/kernel/sdc.cc +++ b/common/kernel/sdc.cc @@ -28,6 +28,22 @@ 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 @@ -59,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) { @@ -251,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 &arguments) { std::vector nets; @@ -318,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(); } @@ -369,8 +456,8 @@ struct SDCParser SdcValue cmd_set_false_path(const std::vector &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); @@ -396,32 +483,17 @@ struct SDCParser 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; - } + 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{}; } From 0e848d4fe8892e3962ef44d551b4b43582650b2c Mon Sep 17 00:00:00 2001 From: Rowan Goemans Date: Thu, 26 Sep 2024 18:37:32 +0200 Subject: [PATCH 9/9] sdc: Allow multiple entities in from/to --- common/kernel/sdc.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/common/kernel/sdc.cc b/common/kernel/sdc.cc index bc93da9b10..7e1771877a 100644 --- a/common/kernel/sdc.cc +++ b/common/kernel/sdc.cc @@ -477,13 +477,9 @@ 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); + for (const auto &ety : val.list) { + sdc_into_path_constraint(ety, is_from, ct); } - - auto &ety = val.list.at(0); - - sdc_into_path_constraint(ety, is_from, ct); } }