From a38ee0786a66f91a702c3eeeed4efd76ad04aec9 Mon Sep 17 00:00:00 2001 From: rowanG077 Date: Wed, 1 Feb 2023 18:41:12 +0100 Subject: ecp5: Propagate clock constraints through DSCS --- ecp5/pack.cc | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 107 insertions(+), 12 deletions(-) (limited to 'ecp5/pack.cc') diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 79d1688f..1146181b 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -2602,6 +2602,15 @@ class Ecp5Packer auto MHz = [&](delay_t a) { return 1000.0 / ctx->getDelayNS(a); }; auto equals_epsilon = [](delay_t a, delay_t b) { return (std::abs(a - b) / std::max(double(b), 1.0)) < 1e-3; }; + auto equals_epsilon_pair = [&](DelayPair& a, DelayPair& b) { + return equals_epsilon(a.min_delay, b.min_delay) + && equals_epsilon(a.max_delay, b.max_delay); + }; + auto equals_epsilon_constr = [&](ClockConstraint& a, ClockConstraint& b) { + return equals_epsilon_pair(a.high, b.high) + && equals_epsilon_pair(a.low, b.low) + && equals_epsilon_pair(a.period, b.period); + }; pool user_constrained, changed_nets; for (auto &net : ctx->nets) { @@ -2619,24 +2628,30 @@ class Ecp5Packer return true; }; - auto set_period = [&](CellInfo *ci, IdString port, delay_t period) { + auto simple_clk_contraint = [&](delay_t period) { + auto constr = std::unique_ptr(new ClockConstraint()); + constr->low = DelayPair(period / 2); + constr->high = DelayPair(period / 2); + constr->period = DelayPair(period); + + return constr; + }; + + auto set_constraint = [&](CellInfo *ci, IdString port, std::unique_ptr constr) { if (!ci->ports.count(port)) return; NetInfo *to = ci->ports.at(port).net; if (to == nullptr) return; if (to->clkconstr != nullptr) { - if (!equals_epsilon(to->clkconstr->period.minDelay(), period) && user_constrained.count(to->name)) + if (!equals_epsilon_constr(*to->clkconstr, *constr) && user_constrained.count(to->name)) log_warning( " Overriding derived constraint of %.1f MHz on net %s with user-specified constraint of " "%.1f MHz.\n", - MHz(to->clkconstr->period.min_delay), to->name.c_str(ctx), MHz(period)); + MHz(to->clkconstr->period.min_delay), to->name.c_str(ctx), MHz(constr->period.min_delay)); return; } - to->clkconstr = std::unique_ptr(new ClockConstraint()); - to->clkconstr->low = DelayPair(period / 2); - to->clkconstr->high = DelayPair(period / 2); - to->clkconstr->period = DelayPair(period); + to->clkconstr = std::move(constr); log_info(" Derived frequency constraint of %.1f MHz for net %s\n", MHz(to->clkconstr->period.minDelay()), to->name.c_str(ctx)); changed_nets.insert(to->name); @@ -2706,6 +2721,86 @@ class Ecp5Packer copy_constraint(ci, id_CLK1, id_ECSOUT, 1); } else if (ci->type == id_DCCA) { copy_constraint(ci, id_CLKI, id_CLKO, 1); + } else if (ci->type == id_DCSC) { + if ((!ci->ports.count(id_CLK0) && !ci->ports.count(id_CLK1)) || !ci->ports.count(id_DCSOUT)) + continue; + auto mode = str_or_default(ci->params, id_DCSMODE, "POS"); + + // TODO: We can do this simplification if we know + // MODESEL is tied to 0. I don't know how I can check this + // if (mode == "CLK0_LOW" || mode == "CLK0_HIGH" || mode == "CLK0") { + // copy_constraint(ci, id_CLK0, id_DCSOUT, 1.0); + // continue; + // } else if (mode == "CLK1_LOW" || mode == "CLK1_HIGH" || mode == "CLK1") { + // copy_constraint(ci, id_CLK1, id_DCSOUT, 1.0); + // continue; + // } else if (mode == "LOW" || mode == "HIGH") { + // continue; + // } + + std::unique_ptr derived_constr = nullptr; + std::vector in_ports = { + ci->ports.at(id_CLK0).net, + ci->ports.at(id_CLK1).net, + }; + + for (auto p : in_ports) { + if (p == nullptr || p->clkconstr == nullptr) { + derived_constr = nullptr; + break; + } + + if (derived_constr == nullptr) { + derived_constr = std::unique_ptr(new ClockConstraint ()); + derived_constr->low = p->clkconstr->low; + derived_constr->high = p->clkconstr->high; + derived_constr->period = p->clkconstr->period; + continue; + } + + auto& c1 = p->clkconstr; + auto& c2 = derived_constr; + + if (mode == "NEG") { + derived_constr->low = DelayPair( + std::min(c1->low.min_delay, c2->low.min_delay), + std::max( + c1->low.max_delay + c2->period.max_delay, + c2->low.max_delay + c1->period.max_delay + ) + ); + } else { + derived_constr->low = DelayPair( + std::min(c1->low.min_delay, c2->low.min_delay), + std::max(c1->low.max_delay, c2->low.max_delay) + ); + } + + if (mode == "POS") { + derived_constr->high = DelayPair( + std::min(c1->high.min_delay, c2->high.min_delay), + std::max( + c1->high.max_delay + c2->period.max_delay, + c2->high.max_delay + c1->period.max_delay + ) + ); + } else { + derived_constr->high = DelayPair( + std::min(c1->high.min_delay, c2->high.min_delay), + std::max(c1->high.max_delay, c2->high.max_delay) + ); + } + + derived_constr->period = DelayPair( + std::min(c1->period.min_delay, c2->period.min_delay), + std::max(c1->period.max_delay, c2->period.max_delay) + ); + } + + if (derived_constr != nullptr) { + set_constraint(ci, id_DCSOUT, std::move(derived_constr)); + } + } else if (ci->type == id_EHXPLLL) { delay_t period_in; if (!get_period(ci, id_CLKI, period_in)) @@ -2734,13 +2829,13 @@ class Ecp5Packer log_info(" Derived VCO frequency %.1f MHz of PLL '%s' is out of legal range [400MHz, " "800MHz]\n", vco_freq, ci->name.c_str(ctx)); - set_period(ci, id_CLKOP, vco_period * int_or_default(ci->params, id_CLKOP_DIV, 1)); - set_period(ci, id_CLKOS, vco_period * int_or_default(ci->params, id_CLKOS_DIV, 1)); - set_period(ci, id_CLKOS2, vco_period * int_or_default(ci->params, id_CLKOS2_DIV, 1)); - set_period(ci, id_CLKOS3, vco_period * int_or_default(ci->params, id_CLKOS3_DIV, 1)); + set_constraint(ci, id_CLKOP, simple_clk_contraint(vco_period * int_or_default(ci->params, id_CLKOP_DIV, 1))); + set_constraint(ci, id_CLKOS, simple_clk_contraint(vco_period * int_or_default(ci->params, id_CLKOS_DIV, 1))); + set_constraint(ci, id_CLKOS2, simple_clk_contraint(vco_period * int_or_default(ci->params, id_CLKOS2_DIV, 1))); + set_constraint(ci, id_CLKOS3, simple_clk_contraint(vco_period * int_or_default(ci->params, id_CLKOS3_DIV, 1))); } else if (ci->type == id_OSCG) { int div = int_or_default(ci->params, id_DIV, 128); - set_period(ci, id_OSC, delay_t((1.0e6 / (2.0 * 155)) * div)); + set_constraint(ci, id_OSC, simple_clk_contraint(delay_t((1.0e6 / (2.0 * 155)) * div))); } } } -- cgit v1.2.3 From d2bf44ba45370af601e1db9fe1cb02d3e58c8e27 Mon Sep 17 00:00:00 2001 From: rowanG077 Date: Mon, 6 Feb 2023 15:40:28 +0100 Subject: ecp5: DSCS clock propagation if modesel is 0 constant --- ecp5/pack.cc | 151 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 99 insertions(+), 52 deletions(-) (limited to 'ecp5/pack.cc') diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 1146181b..7033319f 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -36,6 +36,18 @@ static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) cell->type == ctx->id("$nextpnr_iobuf"); } +static bool net_is_constant(const Context *ctx, NetInfo *net, bool &value) +{ + if (net == nullptr) + return false; + if (net->name == ctx->id("$PACKER_GND_NET") || net->name == ctx->id("$PACKER_VCC_NET")) { + value = (net->name == ctx->id("$PACKER_VCC_NET")); + return true; + } else { + return false; + } +} + class Ecp5Packer { public: @@ -2724,19 +2736,22 @@ class Ecp5Packer } else if (ci->type == id_DCSC) { if ((!ci->ports.count(id_CLK0) && !ci->ports.count(id_CLK1)) || !ci->ports.count(id_DCSOUT)) continue; + auto mode = str_or_default(ci->params, id_DCSMODE, "POS"); + bool mode_constant = false; + auto mode_is_constant = net_is_constant(ctx, ci->ports.at(id_MODESEL).net, mode_constant); - // TODO: We can do this simplification if we know - // MODESEL is tied to 0. I don't know how I can check this - // if (mode == "CLK0_LOW" || mode == "CLK0_HIGH" || mode == "CLK0") { - // copy_constraint(ci, id_CLK0, id_DCSOUT, 1.0); - // continue; - // } else if (mode == "CLK1_LOW" || mode == "CLK1_HIGH" || mode == "CLK1") { - // copy_constraint(ci, id_CLK1, id_DCSOUT, 1.0); - // continue; - // } else if (mode == "LOW" || mode == "HIGH") { - // continue; - // } + if (mode_is_constant && mode_constant == false) { + if (mode == "CLK0_LOW" || mode == "CLK0_HIGH" || mode == "CLK0") { + copy_constraint(ci, id_CLK0, id_DCSOUT, 1.0); + continue; + } else if (mode == "CLK1_LOW" || mode == "CLK1_HIGH" || mode == "CLK1") { + copy_constraint(ci, id_CLK1, id_DCSOUT, 1.0); + continue; + } else if (mode == "LOW" || mode == "HIGH") { + continue; + } + } std::unique_ptr derived_constr = nullptr; std::vector in_ports = { @@ -2744,63 +2759,95 @@ class Ecp5Packer ci->ports.at(id_CLK1).net, }; - for (auto p : in_ports) { - if (p == nullptr || p->clkconstr == nullptr) { + // Generate all unique clock pairs find the worst + // constraint from switching between them and merge them + // into the final output constraint. + for (size_t i = 0; i < in_ports.size(); ++i) { + auto p1 = in_ports[i]; + if (p1 == nullptr || p1->clkconstr == nullptr) { derived_constr = nullptr; break; } + for (size_t j = i + 1; j < in_ports.size(); ++j) { + auto p2 = in_ports[j]; + if (p2 == nullptr || p2->clkconstr == nullptr) { + break; + } + auto& c1 = p1->clkconstr; + auto& c2 = p2->clkconstr; + + auto merged_constr = std::unique_ptr(new ClockConstraint()); + + if (mode == "NEG") { + merged_constr->low = DelayPair( + std::min(c1->low.min_delay, c2->low.min_delay), + std::max( + c1->low.max_delay + c2->period.max_delay, + c2->low.max_delay + c1->period.max_delay + ) + ); + } else { + merged_constr->low = DelayPair( + std::min(c1->low.min_delay, c2->low.min_delay), + std::max(c1->low.max_delay, c2->low.max_delay) + ); + } - if (derived_constr == nullptr) { - derived_constr = std::unique_ptr(new ClockConstraint ()); - derived_constr->low = p->clkconstr->low; - derived_constr->high = p->clkconstr->high; - derived_constr->period = p->clkconstr->period; - continue; - } + if (mode == "POS") { + merged_constr->high = DelayPair( + std::min(c1->high.min_delay, c2->high.min_delay), + std::max( + c1->high.max_delay + c2->period.max_delay, + c2->high.max_delay + c1->period.max_delay + ) + ); + } else { + merged_constr->high = DelayPair( + std::min(c1->high.min_delay, c2->high.min_delay), + std::max(c1->high.max_delay, c2->high.max_delay) + ); + } - auto& c1 = p->clkconstr; - auto& c2 = derived_constr; + merged_constr->period = DelayPair( + std::min(c1->period.min_delay, c2->period.min_delay), + std::max(c1->period.max_delay, c2->period.max_delay) + ); + + if (derived_constr == nullptr) { + derived_constr = std::move(merged_constr); + continue; + } - if (mode == "NEG") { - derived_constr->low = DelayPair( - std::min(c1->low.min_delay, c2->low.min_delay), - std::max( - c1->low.max_delay + c2->period.max_delay, - c2->low.max_delay + c1->period.max_delay - ) + derived_constr->period.min_delay = std::min( + derived_constr->period.min_delay, + merged_constr->period.min_delay ); - } else { - derived_constr->low = DelayPair( - std::min(c1->low.min_delay, c2->low.min_delay), - std::max(c1->low.max_delay, c2->low.max_delay) + derived_constr->period.max_delay = std::max( + derived_constr->period.max_delay, + merged_constr->period.max_delay ); - } - - if (mode == "POS") { - derived_constr->high = DelayPair( - std::min(c1->high.min_delay, c2->high.min_delay), - std::max( - c1->high.max_delay + c2->period.max_delay, - c2->high.max_delay + c1->period.max_delay - ) + derived_constr->low.min_delay = std::min( + derived_constr->low.min_delay, + merged_constr->low.min_delay ); - } else { - derived_constr->high = DelayPair( - std::min(c1->high.min_delay, c2->high.min_delay), - std::max(c1->high.max_delay, c2->high.max_delay) + derived_constr->low.max_delay = std::max( + derived_constr->low.max_delay, + merged_constr->low.max_delay + ); + derived_constr->high.min_delay = std::min( + derived_constr->high.min_delay, + merged_constr->high.min_delay + ); + derived_constr->high.max_delay = std::max( + derived_constr->high.max_delay, + merged_constr->high.max_delay ); } - - derived_constr->period = DelayPair( - std::min(c1->period.min_delay, c2->period.min_delay), - std::max(c1->period.max_delay, c2->period.max_delay) - ); } if (derived_constr != nullptr) { set_constraint(ci, id_DCSOUT, std::move(derived_constr)); } - } else if (ci->type == id_EHXPLLL) { delay_t period_in; if (!get_period(ci, id_CLKI, period_in)) -- cgit v1.2.3 From 9e8f8b7b45b4060531190259b2cfaae4d18819f1 Mon Sep 17 00:00:00 2001 From: rowanG077 Date: Mon, 6 Feb 2023 17:04:30 +0100 Subject: streamline constant_net detection --- ecp5/pack.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'ecp5/pack.cc') diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 7033319f..a6f31656 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -38,10 +38,12 @@ static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) static bool net_is_constant(const Context *ctx, NetInfo *net, bool &value) { + auto gnd = ctx->id("$PACKER_GND_NET"); + auto vcc = ctx->id("$PACKER_VCC_NET"); if (net == nullptr) return false; - if (net->name == ctx->id("$PACKER_GND_NET") || net->name == ctx->id("$PACKER_VCC_NET")) { - value = (net->name == ctx->id("$PACKER_VCC_NET")); + if (net->name.in(gnd, vcc)) { + value = (net->name == vcc); return true; } else { return false; -- cgit v1.2.3