From 6afc2c75fdf73493d63c511322ce06dbaa4ab0d7 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 30 Sep 2018 15:12:06 +0100 Subject: ecp5: Adding carry helper functions Signed-off-by: David Shah --- common/design_utils.cc | 19 +++++++++++ common/design_utils.h | 3 ++ ecp5/pack.cc | 86 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/common/design_utils.cc b/common/design_utils.cc index 21c9dcc4..0976d6dd 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -73,4 +73,23 @@ void print_utilisation(const Context *ctx) log_break(); } +// Connect a net to a port +void connect_port(const Context *ctx, NetInfo *net, CellInfo *cell, IdString port_name) { + PortInfo &port = cell->ports.at(port_name); + NPNR_ASSERT(port.net == nullptr); + port.net = net; + if (port.type == PORT_OUT) { + NPNR_ASSERT(net->driver.cell == nullptr); + net->driver.cell = cell; + net->driver.port = port_name; + } else if (port.type == PORT_IN) { + PortRef user; + user.cell = cell; + user.port = port_name; + net->users.push_back(user); + } else { + NPNR_ASSERT_FALSE("invalid port type for connect_port"); + } +} + NEXTPNR_NAMESPACE_END diff --git a/common/design_utils.h b/common/design_utils.h index 95975179..ccf2463b 100644 --- a/common/design_utils.h +++ b/common/design_utils.h @@ -82,6 +82,9 @@ template CellInfo *net_driven_by(const Context *ctx, const NetInfo } } +// Connect a net to a port +void connect_port(const Context *ctx, NetInfo *net, CellInfo *cell, IdString port_name); + void print_utilisation(const Context *ctx); NEXTPNR_NAMESPACE_END diff --git a/ecp5/pack.cc b/ecp5/pack.cc index a2077204..694fd4d9 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -24,7 +24,7 @@ #include "design_utils.h" #include "log.h" #include "util.h" - +#include NEXTPNR_NAMESPACE_BEGIN static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) @@ -312,6 +312,90 @@ class Ecp5Packer flush_cells(); } + // Create a feed in to the carry chain + CellInfo *make_carry_feed_in(NetInfo *carry, PortRef chain_in) { + std::unique_ptr feedin = + create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE")); + + feedin->params[ctx->id("MODE")] = "CCU2"; + feedin->params[ctx->id("LUT0_INITVAL")] = "10"; // LUT4 = 0; LUT2 = A + feedin->params[ctx->id("LUT1_INITVAL")] = "65535"; + feedin->params[ctx->id("INJECT1_0")] = "NO"; + feedin->params[ctx->id("INJECT1_1")] = "YES"; + + carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(), [chain_in](const PortRef &user) { + return user.port == chain_in.port && user.cell == chain_in.cell; + }), carry->users.end()); + connect_port(ctx, carry, feedin.get(), id_A0); + + std::unique_ptr new_carry(new NetInfo()); + new_carry->name = ctx->id(feedin->name.str(ctx) + "$FCO"); + connect_port(ctx, new_carry.get(), feedin.get(), id_FCO); + connect_port(ctx, new_carry.get(), chain_in.cell, chain_in.port); + + CellInfo *feedin_ptr = feedin.get(); + new_cells.push_back(std::move(feedin)); + IdString new_carry_name = new_carry->name; + ctx->nets[new_carry_name] = std::move(new_carry); + return feedin_ptr; + } + + // Create a feed out and loop through from the carry chain + CellInfo *make_carry_feed_out(NetInfo *carry, boost::optional chain_next) { + std::unique_ptr feedout = + create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE")); + feedout->params[ctx->id("MODE")] = "CCU2"; + feedout->params[ctx->id("LUT0_INITVAL")] = "0"; + feedout->params[ctx->id("LUT1_INITVAL")] = "10"; // LUT4 = 0; LUT2 = A + feedout->params[ctx->id("INJECT1_0")] = "YES"; + feedout->params[ctx->id("INJECT1_1")] = "NO"; + + PortRef carry_drv = carry->driver; + carry->driver.cell = nullptr; + connect_port(ctx, carry, feedout.get(), id_F0); + + std::unique_ptr new_cin(new NetInfo()); + new_cin->name = ctx->id(feedout->name.str(ctx) + "$FCI"); + new_cin->driver = carry_drv; + carry_drv.cell->ports.at(carry_drv.port).net = new_cin.get(); + connect_port(ctx, new_cin.get(), feedout.get(), id_FCI); + + if (chain_next) { + // Loop back into LUT4_1 for feedthrough + connect_port(ctx, carry, feedout.get(), id_A1); + + carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(), [chain_next](const PortRef &user) { + return user.port == chain_next->port && user.cell == chain_next->cell; + }), carry->users.end()); + + std::unique_ptr new_cout(new NetInfo()); + new_cout->name = ctx->id(feedout->name.str(ctx) + "$FCO"); + + chain_next->cell->ports[chain_next->port].net = nullptr; + connect_port(ctx, new_cout.get(), chain_next->cell, chain_next->port); + + IdString new_cout_name = new_cout->name; + ctx->nets[new_cout_name] = std::move(new_cout); + + } + + CellInfo *feedout_ptr = feedout.get(); + new_cells.push_back(std::move(feedout)); + + IdString new_cin_name = new_cin->name; + ctx->nets[new_cin_name] = std::move(new_cin); + + return feedout_ptr; + + } + + // Pack carries and set up appropriate relative constraints + void pack_carries() + { + log_info("Packing carries...\n"); + + } + // Pack LUTs that have been paired together void pack_lut_pairs() { -- cgit v1.2.3 From a27c7b45dee834dbf7342df0acff257aab946f25 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 30 Sep 2018 16:29:26 +0100 Subject: Refactor chain finder to its own file Signed-off-by: David Shah --- common/chain_utils.h | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ common/design_utils.cc | 3 ++- ecp5/pack.cc | 40 ++++++++++++++--------------- ice40/chains.cc | 40 +---------------------------- 4 files changed, 90 insertions(+), 61 deletions(-) create mode 100644 common/chain_utils.h diff --git a/common/chain_utils.h b/common/chain_utils.h new file mode 100644 index 00000000..b783e30b --- /dev/null +++ b/common/chain_utils.h @@ -0,0 +1,68 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * + * 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 CHAIN_UTILS_H +#define CHAIN_UTILS_H + +#include "nextpnr.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct CellChain +{ + std::vector cells; +}; + +// Generic chain finder +template +std::vector find_chains(const Context *ctx, F1 cell_type_predicate, F2 get_previous, F3 get_next, + size_t min_length = 2) +{ + std::set chained; + std::vector chains; + for (auto cell : sorted(ctx->cells)) { + if (chained.find(cell.first) != chained.end()) + continue; + CellInfo *ci = cell.second; + if (cell_type_predicate(ctx, ci)) { + CellInfo *start = ci; + CellInfo *prev_start = ci; + while (prev_start != nullptr) { + start = prev_start; + prev_start = get_previous(ctx, start); + } + CellChain chain; + CellInfo *end = start; + while (end != nullptr) { + chain.cells.push_back(end); + end = get_next(ctx, end); + } + if (chain.cells.size() >= min_length) { + chains.push_back(chain); + for (auto c : chain.cells) + chained.insert(c->name); + } + } + } + return chains; +} + +NEXTPNR_NAMESPACE_END +#endif diff --git a/common/design_utils.cc b/common/design_utils.cc index 0976d6dd..7ad7f749 100644 --- a/common/design_utils.cc +++ b/common/design_utils.cc @@ -74,7 +74,8 @@ void print_utilisation(const Context *ctx) } // Connect a net to a port -void connect_port(const Context *ctx, NetInfo *net, CellInfo *cell, IdString port_name) { +void connect_port(const Context *ctx, NetInfo *net, CellInfo *cell, IdString port_name) +{ PortInfo &port = cell->ports.at(port_name); NPNR_ASSERT(port.net == nullptr); port.net = net; diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 694fd4d9..e2bc3875 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -18,13 +18,13 @@ */ #include +#include #include #include #include "cells.h" #include "design_utils.h" #include "log.h" #include "util.h" -#include NEXTPNR_NAMESPACE_BEGIN static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) @@ -313,9 +313,9 @@ class Ecp5Packer } // Create a feed in to the carry chain - CellInfo *make_carry_feed_in(NetInfo *carry, PortRef chain_in) { - std::unique_ptr feedin = - create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE")); + CellInfo *make_carry_feed_in(NetInfo *carry, PortRef chain_in) + { + std::unique_ptr feedin = create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE")); feedin->params[ctx->id("MODE")] = "CCU2"; feedin->params[ctx->id("LUT0_INITVAL")] = "10"; // LUT4 = 0; LUT2 = A @@ -323,9 +323,11 @@ class Ecp5Packer feedin->params[ctx->id("INJECT1_0")] = "NO"; feedin->params[ctx->id("INJECT1_1")] = "YES"; - carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(), [chain_in](const PortRef &user) { - return user.port == chain_in.port && user.cell == chain_in.cell; - }), carry->users.end()); + carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(), + [chain_in](const PortRef &user) { + return user.port == chain_in.port && user.cell == chain_in.cell; + }), + carry->users.end()); connect_port(ctx, carry, feedin.get(), id_A0); std::unique_ptr new_carry(new NetInfo()); @@ -341,9 +343,9 @@ class Ecp5Packer } // Create a feed out and loop through from the carry chain - CellInfo *make_carry_feed_out(NetInfo *carry, boost::optional chain_next) { - std::unique_ptr feedout = - create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE")); + CellInfo *make_carry_feed_out(NetInfo *carry, boost::optional chain_next) + { + std::unique_ptr feedout = create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE")); feedout->params[ctx->id("MODE")] = "CCU2"; feedout->params[ctx->id("LUT0_INITVAL")] = "0"; feedout->params[ctx->id("LUT1_INITVAL")] = "10"; // LUT4 = 0; LUT2 = A @@ -364,9 +366,11 @@ class Ecp5Packer // Loop back into LUT4_1 for feedthrough connect_port(ctx, carry, feedout.get(), id_A1); - carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(), [chain_next](const PortRef &user) { - return user.port == chain_next->port && user.cell == chain_next->cell; - }), carry->users.end()); + carry->users.erase(std::remove_if(carry->users.begin(), carry->users.end(), + [chain_next](const PortRef &user) { + return user.port == chain_next->port && user.cell == chain_next->cell; + }), + carry->users.end()); std::unique_ptr new_cout(new NetInfo()); new_cout->name = ctx->id(feedout->name.str(ctx) + "$FCO"); @@ -376,8 +380,7 @@ class Ecp5Packer IdString new_cout_name = new_cout->name; ctx->nets[new_cout_name] = std::move(new_cout); - - } + } CellInfo *feedout_ptr = feedout.get(); new_cells.push_back(std::move(feedout)); @@ -386,15 +389,10 @@ class Ecp5Packer ctx->nets[new_cin_name] = std::move(new_cin); return feedout_ptr; - } // Pack carries and set up appropriate relative constraints - void pack_carries() - { - log_info("Packing carries...\n"); - - } + void pack_carries() { log_info("Packing carries...\n"); } // Pack LUTs that have been paired together void pack_lut_pairs() diff --git a/ice40/chains.cc b/ice40/chains.cc index e8a7ab17..fb361d2d 100644 --- a/ice40/chains.cc +++ b/ice40/chains.cc @@ -21,6 +21,7 @@ #include #include #include "cells.h" +#include "chain_utils.h" #include "design_utils.h" #include "log.h" #include "place_common.h" @@ -28,45 +29,6 @@ NEXTPNR_NAMESPACE_BEGIN -struct CellChain -{ - std::vector cells; -}; - -// Generic chain finder -template -std::vector find_chains(const Context *ctx, F1 cell_type_predicate, F2 get_previous, F3 get_next, - size_t min_length = 2) -{ - std::set chained; - std::vector chains; - for (auto cell : sorted(ctx->cells)) { - if (chained.find(cell.first) != chained.end()) - continue; - CellInfo *ci = cell.second; - if (cell_type_predicate(ctx, ci)) { - CellInfo *start = ci; - CellInfo *prev_start = ci; - while (prev_start != nullptr) { - start = prev_start; - prev_start = get_previous(ctx, start); - } - CellChain chain; - CellInfo *end = start; - while (end != nullptr) { - chain.cells.push_back(end); - end = get_next(ctx, end); - } - if (chain.cells.size() >= min_length) { - chains.push_back(chain); - for (auto c : chain.cells) - chained.insert(c->name); - } - } - } - return chains; -} - class ChainConstrainer { private: -- cgit v1.2.3 From 2628344298da8b86fd55a107cd3f0543dd5bfbc1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 30 Sep 2018 16:58:02 +0100 Subject: ecp5: Support code for carry chain handling Signed-off-by: David Shah --- ecp5/cells.cc | 22 ++++++++++++ ecp5/pack.cc | 112 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 118 insertions(+), 16 deletions(-) diff --git a/ecp5/cells.cc b/ecp5/cells.cc index c7afdbc2..4465d4e4 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -124,6 +124,28 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str add_port(ctx, new_cell.get(), "C", PORT_IN); add_port(ctx, new_cell.get(), "D", PORT_IN); add_port(ctx, new_cell.get(), "Z", PORT_OUT); + } else if (type == ctx->id("CCU2C")) { + new_cell->params[ctx->id("INIT0")] = "0"; + new_cell->params[ctx->id("INIT1")] = "0"; + new_cell->params[ctx->id("INJECT1_0")] = "YES"; + new_cell->params[ctx->id("INJECT1_1")] = "YES"; + + add_port(ctx, new_cell.get(), "CIN", PORT_IN); + + add_port(ctx, new_cell.get(), "A0", PORT_IN); + add_port(ctx, new_cell.get(), "B0", PORT_IN); + add_port(ctx, new_cell.get(), "C0", PORT_IN); + add_port(ctx, new_cell.get(), "D0", PORT_IN); + + add_port(ctx, new_cell.get(), "A1", PORT_IN); + add_port(ctx, new_cell.get(), "B1", PORT_IN); + add_port(ctx, new_cell.get(), "C1", PORT_IN); + add_port(ctx, new_cell.get(), "D1", PORT_IN); + + add_port(ctx, new_cell.get(), "S0", PORT_OUT); + add_port(ctx, new_cell.get(), "S1", PORT_OUT); + add_port(ctx, new_cell.get(), "COUT", PORT_OUT); + } else if (type == ctx->id("DCCA")) { add_port(ctx, new_cell.get(), "CLKI", PORT_IN); add_port(ctx, new_cell.get(), "CLKO", PORT_OUT); diff --git a/ecp5/pack.cc b/ecp5/pack.cc index e2bc3875..d2c0085b 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -22,6 +22,7 @@ #include #include #include "cells.h" +#include "chain_utils.h" #include "design_utils.h" #include "log.h" #include "util.h" @@ -106,6 +107,26 @@ class Ecp5Packer return true; } + // Return whether or not an FF can be added to a tile (pairing checks must also be done using the fn above) + bool can_add_ff_to_file(const std::vector &tile_ffs, CellInfo *ff0) { + for (const auto &existing : tile_ffs) { + if (net_or_nullptr(existing, ctx->id("CLK")) != net_or_nullptr(ff0, ctx->id("CLK"))) + return false; + if (net_or_nullptr(existing, ctx->id("LSR")) != net_or_nullptr(ff0, ctx->id("LSR"))) + return false; + if (str_or_default(existing->params, ctx->id("CLKMUX"), "CLK") != + str_or_default(ff0->params, ctx->id("CLKMUX"), "CLK")) + return false; + if (str_or_default(existing->params, ctx->id("LSRMUX"), "LSR") != + str_or_default(ff0->params, ctx->id("LSRMUX"), "LSR")) + return false; + if (str_or_default(existing->params, ctx->id("LSRMUX"), "LSR") != + str_or_default(ff0->params, ctx->id("LSRMUX"), "LSR")) + return false; + } + return true; + } + // Return true if two LUTs can be paired considering FF compatibility bool can_pack_lutff(IdString lut0, IdString lut1) { @@ -315,11 +336,10 @@ class Ecp5Packer // Create a feed in to the carry chain CellInfo *make_carry_feed_in(NetInfo *carry, PortRef chain_in) { - std::unique_ptr feedin = create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE")); + std::unique_ptr feedin = create_ecp5_cell(ctx, ctx->id("CCU2C")); - feedin->params[ctx->id("MODE")] = "CCU2"; - feedin->params[ctx->id("LUT0_INITVAL")] = "10"; // LUT4 = 0; LUT2 = A - feedin->params[ctx->id("LUT1_INITVAL")] = "65535"; + feedin->params[ctx->id("INIT0")] = "10"; // LUT4 = 0; LUT2 = A + feedin->params[ctx->id("INIT1")] = "65535"; feedin->params[ctx->id("INJECT1_0")] = "NO"; feedin->params[ctx->id("INJECT1_1")] = "YES"; @@ -331,8 +351,8 @@ class Ecp5Packer connect_port(ctx, carry, feedin.get(), id_A0); std::unique_ptr new_carry(new NetInfo()); - new_carry->name = ctx->id(feedin->name.str(ctx) + "$FCO"); - connect_port(ctx, new_carry.get(), feedin.get(), id_FCO); + new_carry->name = ctx->id(feedin->name.str(ctx) + "$COUT"); + connect_port(ctx, new_carry.get(), feedin.get(), ctx->id("COUT")); connect_port(ctx, new_carry.get(), chain_in.cell, chain_in.port); CellInfo *feedin_ptr = feedin.get(); @@ -343,24 +363,23 @@ class Ecp5Packer } // Create a feed out and loop through from the carry chain - CellInfo *make_carry_feed_out(NetInfo *carry, boost::optional chain_next) + CellInfo *make_carry_feed_out(NetInfo *carry, boost::optional chain_next = boost::optional()) { - std::unique_ptr feedout = create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE")); - feedout->params[ctx->id("MODE")] = "CCU2"; - feedout->params[ctx->id("LUT0_INITVAL")] = "0"; - feedout->params[ctx->id("LUT1_INITVAL")] = "10"; // LUT4 = 0; LUT2 = A + std::unique_ptr feedout = create_ecp5_cell(ctx, ctx->id("CCU2C")); + feedout->params[ctx->id("INIT0")] = "0"; + feedout->params[ctx->id("INIT1")] = "10"; // LUT4 = 0; LUT2 = A feedout->params[ctx->id("INJECT1_0")] = "YES"; feedout->params[ctx->id("INJECT1_1")] = "NO"; PortRef carry_drv = carry->driver; carry->driver.cell = nullptr; - connect_port(ctx, carry, feedout.get(), id_F0); + connect_port(ctx, carry, feedout.get(), ctx->id("S0")); std::unique_ptr new_cin(new NetInfo()); - new_cin->name = ctx->id(feedout->name.str(ctx) + "$FCI"); + new_cin->name = ctx->id(feedout->name.str(ctx) + "$CIN"); new_cin->driver = carry_drv; carry_drv.cell->ports.at(carry_drv.port).net = new_cin.get(); - connect_port(ctx, new_cin.get(), feedout.get(), id_FCI); + connect_port(ctx, new_cin.get(), feedout.get(), ctx->id("CIN")); if (chain_next) { // Loop back into LUT4_1 for feedthrough @@ -373,7 +392,8 @@ class Ecp5Packer carry->users.end()); std::unique_ptr new_cout(new NetInfo()); - new_cout->name = ctx->id(feedout->name.str(ctx) + "$FCO"); + new_cout->name = ctx->id(feedout->name.str(ctx) + "$COUT"); + connect_port(ctx, new_cout.get(), feedout.get(), ctx->id("COUT")); chain_next->cell->ports[chain_next->port].net = nullptr; connect_port(ctx, new_cout.get(), chain_next->cell, chain_next->port); @@ -391,8 +411,68 @@ class Ecp5Packer return feedout_ptr; } + // Split a carry chain into multiple legal chains + std::vector split_carry_chain(CellChain &carryc) + { + bool start_of_chain = true; + std::vector chains; + const int max_length = (ctx->chip_info->width - 4) * 4 - 2; + auto curr_cell = carryc.cells.begin(); + while (curr_cell != carryc.cells.end()) { + CellInfo *cell = *curr_cell; + if (start_of_chain) { + chains.emplace_back(); + start_of_chain = false; + if (cell->ports.at(ctx->id("CIN")).net) { + // CIN is not constant and not part of a chain. Must feed in from fabric + PortRef inport; + inport.cell = cell; + inport.port = ctx->id("CIN"); + CellInfo *feedin = make_carry_feed_in(cell->ports.at(ctx->id("CIN")).net, inport); + chains.back().cells.push_back(feedin); + } + } + chains.back().cells.push_back(cell); + bool split_chain = int(chains.back().cells.size()) > max_length; + if (split_chain) { + CellInfo *passout = make_carry_feed_out(cell->ports.at(ctx->id("COUT")).net); + chains.back().cells.back() = passout; + start_of_chain = true; + } else { + NetInfo *carry_net = cell->ports.at(ctx->id("COUT")).net; + bool at_end = (curr_cell == carryc.cells.end() - 1); + if (carry_net != nullptr && (carry_net->users.size() > 1 || at_end)) { + boost::optional nextport; + if (!at_end) { + auto next_cell = *(curr_cell + 1); + PortRef nextpr; + nextpr.cell = next_cell; + nextpr.port = ctx->id("CIN"); + nextport = nextpr; + } + CellInfo *passout = make_carry_feed_out(cell->ports.at(ctx->id("COUT")).net, nextport); + chains.back().cells.push_back(passout); + } + ++curr_cell; + } + } + return chains; + } + + // Pack carries and set up appropriate relative constraints - void pack_carries() { log_info("Packing carries...\n"); } + void pack_carries() { + log_info("Packing carries...\n"); + auto chains = find_chains(ctx, [](const Context *ctx, const CellInfo *cell) { + return is_carry(ctx, cell); + }, [](const Context *ctx, const CellInfo *cell) { + return net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_carry, ctx->id("COUT")); + }, [](const Context *ctx, const CellInfo *cell) { + return net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_carry, + ctx->id("CIN"), false); + }, 1); + + } // Pack LUTs that have been paired together void pack_lut_pairs() -- cgit v1.2.3 From e81a95cf7e7c58f3a5ad5e3e9414b640e7a82232 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 30 Sep 2018 17:05:42 +0100 Subject: ecp5: Add ccu2c_to_slice Signed-off-by: David Shah --- ecp5/cells.cc | 27 +++++++++++++++++++++++++++ ecp5/cells.h | 1 + ecp5/pack.cc | 25 +++++++++++++------------ 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 4465d4e4..4a83bb30 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -211,4 +211,31 @@ void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index) replace_port(lut, ctx->id("Z"), lc, ctx->id("F" + std::to_string(index))); } +void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc) +{ + lc->params[ctx->id("MODE")] = "CCU2C"; + lc->params[ctx->id("LUT0_INITVAL")] = str_or_default(ccu->params, ctx->id("INIT0"), "0"); + lc->params[ctx->id("LUT1_INITVAL")] = str_or_default(ccu->params, ctx->id("INIT1"), "0"); + + lc->params[ctx->id("INJECT1_0")] = str_or_default(ccu->params, ctx->id("INJECT1_0"), "YES"); + lc->params[ctx->id("INJECT1_1")] = str_or_default(ccu->params, ctx->id("INJECT1_1"), "YES"); + + replace_port(ccu, ctx->id("CIN"), lc, ctx->id("FCI")); + + replace_port(ccu, ctx->id("A0"), lc, ctx->id("A0")); + replace_port(ccu, ctx->id("B0"), lc, ctx->id("B0")); + replace_port(ccu, ctx->id("C0"), lc, ctx->id("C0")); + replace_port(ccu, ctx->id("D0"), lc, ctx->id("D0")); + + replace_port(ccu, ctx->id("A1"), lc, ctx->id("A1")); + replace_port(ccu, ctx->id("B1"), lc, ctx->id("B1")); + replace_port(ccu, ctx->id("C1"), lc, ctx->id("C1")); + replace_port(ccu, ctx->id("D1"), lc, ctx->id("D1")); + + replace_port(ccu, ctx->id("S0"), lc, ctx->id("F0")); + replace_port(ccu, ctx->id("S1"), lc, ctx->id("F1")); + + replace_port(ccu, ctx->id("COUT"), lc, ctx->id("FCO")); +} + NEXTPNR_NAMESPACE_END diff --git a/ecp5/cells.h b/ecp5/cells.h index b0c74ca9..d2ea5490 100644 --- a/ecp5/cells.h +++ b/ecp5/cells.h @@ -48,6 +48,7 @@ inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->ty void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut); void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index); +void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc); NEXTPNR_NAMESPACE_END diff --git a/ecp5/pack.cc b/ecp5/pack.cc index d2c0085b..b17f8f20 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -108,7 +108,8 @@ class Ecp5Packer } // Return whether or not an FF can be added to a tile (pairing checks must also be done using the fn above) - bool can_add_ff_to_file(const std::vector &tile_ffs, CellInfo *ff0) { + bool can_add_ff_to_file(const std::vector &tile_ffs, CellInfo *ff0) + { for (const auto &existing : tile_ffs) { if (net_or_nullptr(existing, ctx->id("CLK")) != net_or_nullptr(ff0, ctx->id("CLK"))) return false; @@ -459,19 +460,19 @@ class Ecp5Packer return chains; } - // Pack carries and set up appropriate relative constraints - void pack_carries() { + void pack_carries() + { log_info("Packing carries...\n"); - auto chains = find_chains(ctx, [](const Context *ctx, const CellInfo *cell) { - return is_carry(ctx, cell); - }, [](const Context *ctx, const CellInfo *cell) { - return net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_carry, ctx->id("COUT")); - }, [](const Context *ctx, const CellInfo *cell) { - return net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_carry, - ctx->id("CIN"), false); - }, 1); - + auto chains = find_chains( + ctx, [](const Context *ctx, const CellInfo *cell) { return is_carry(ctx, cell); }, + [](const Context *ctx, const CellInfo *cell) { + return net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_carry, ctx->id("COUT")); + }, + [](const Context *ctx, const CellInfo *cell) { + return net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_carry, ctx->id("CIN"), false); + }, + 1); } // Pack LUTs that have been paired together -- cgit v1.2.3 From fef29d87626d9030f54afafa92db744ea9f21741 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 30 Sep 2018 17:18:30 +0100 Subject: ecp5: First stages of carry packing Signed-off-by: David Shah --- ecp5/pack.cc | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index b17f8f20..7a7e0dda 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -357,7 +357,8 @@ class Ecp5Packer connect_port(ctx, new_carry.get(), chain_in.cell, chain_in.port); CellInfo *feedin_ptr = feedin.get(); - new_cells.push_back(std::move(feedin)); + IdString feedin_name = feedin->name; + ctx->cells[feedin_name] = std::move(feedin); IdString new_carry_name = new_carry->name; ctx->nets[new_carry_name] = std::move(new_carry); return feedin_ptr; @@ -404,7 +405,8 @@ class Ecp5Packer } CellInfo *feedout_ptr = feedout.get(); - new_cells.push_back(std::move(feedout)); + IdString feedout_name = feedout->name; + ctx->cells[feedout_name] = std::move(feedout); IdString new_cin_name = new_cin->name; ctx->nets[new_cin_name] = std::move(new_cin); @@ -464,7 +466,8 @@ class Ecp5Packer void pack_carries() { log_info("Packing carries...\n"); - auto chains = find_chains( + // Find all chains (including single carry cells) + auto carry_chains = find_chains( ctx, [](const Context *ctx, const CellInfo *cell) { return is_carry(ctx, cell); }, [](const Context *ctx, const CellInfo *cell) { return net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_carry, ctx->id("COUT")); @@ -473,6 +476,63 @@ class Ecp5Packer return net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_carry, ctx->id("CIN"), false); }, 1); + std::vector all_chains; + + // Chain splitting + for (auto &base_chain : carry_chains) { + if (ctx->verbose) { + log_info("Found carry chain: \n"); + for (auto entry : base_chain.cells) + log_info(" %s\n", entry->name.c_str(ctx)); + log_info("\n"); + } + std::vector split_chains = split_carry_chain(base_chain); + for (auto &chain : split_chains) { + all_chains.push_back(chain); + } + } + + // Chain packing + for (auto &chain : all_chains) { + int cell_count = 0; + std::vector tile_ffs; + for (auto &cell : chain.cells) { + if (cell_count % 4 == 0) + tile_ffs.clear(); + std::unique_ptr slice = + create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), cell->name.str(ctx) + "$CCU2_SLICE"); + + ccu2c_to_slice(ctx, cell, slice.get()); + + CellInfo *ff0 = nullptr; + NetInfo *f0net = slice->ports.at(ctx->id("F0")).net; + if (f0net != nullptr) { + ff0 = net_only_drives(ctx, f0net, is_ff, ctx->id("DI"), false); + if (ff0 != nullptr && can_add_ff_to_file(tile_ffs, ff0)) { + ff_to_slice(ctx, ff0, slice.get(), 0, true); + tile_ffs.push_back(ff0); + packed_cells.insert(ff0->name); + } + } + + CellInfo *ff1 = nullptr; + NetInfo *f1net = slice->ports.at(ctx->id("F1")).net; + if (f1net != nullptr) { + ff1 = net_only_drives(ctx, f1net, is_ff, ctx->id("DI"), false); + if (ff1 != nullptr && (ff0 == nullptr || can_pack_ffs(ff0, ff1)) && + can_add_ff_to_file(tile_ffs, ff1)) { + ff_to_slice(ctx, ff1, slice.get(), 1, true); + tile_ffs.push_back(ff1); + packed_cells.insert(ff1->name); + } + } + + new_cells.push_back(std::move(slice)); + packed_cells.insert(cell->name); + cell_count++; + } + } + flush_cells(); } // Pack LUTs that have been paired together -- cgit v1.2.3 From 9218d2e56b5d06a57f80e7270b1538d134f8a6fa Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 30 Sep 2018 17:42:47 +0100 Subject: ecp5: Relative placement and bitstream gen for carries Signed-off-by: David Shah --- ecp5/bitstream.cc | 17 ++++++++++++++++- ecp5/cells.cc | 2 +- ecp5/pack.cc | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index f04b1269..5e851fbf 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -256,7 +256,22 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex cc.tiles[tname].add_enum("LSR1.SRMODE", str_or_default(ci->params, ctx->id("SRMODE"), "LSR_OVER_CE")); cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, ctx->id("LSRMUX"), "LSR")); } - // TODO: CLKMUX, CEMUX, carry + + if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "CCU2") { + cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0", + str_or_default(ci->params, ctx->id("INJECT1_0"), "YES")); + cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1", + str_or_default(ci->params, ctx->id("INJECT1_1"), "YES")); + } + + // Tie unused inputs high + for (auto input : {id_A0, id_B0, id_C0, id_D0, id_A1, id_B1, id_C1, id_D1}) { + if (ci->ports.find(input) == ci->ports.end() || ci->ports.at(input).net == nullptr) { + cc.tiles[tname].add_enum(slice + "." + input.str(ctx) + "MUX", "1"); + } + } + + // TODO: CLKMUX } else if (ci->type == ctx->id("TRELLIS_IO")) { std::string pio = ctx->locInfo(bel)->bel_data[bel.index].name.get(); std::string iotype = str_or_default(ci->attrs, ctx->id("IO_TYPE"), "LVCMOS33"); diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 4a83bb30..7e101ab2 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -213,7 +213,7 @@ void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index) void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc) { - lc->params[ctx->id("MODE")] = "CCU2C"; + lc->params[ctx->id("MODE")] = "CCU2"; lc->params[ctx->id("LUT0_INITVAL")] = str_or_default(ccu->params, ctx->id("INIT0"), "0"); lc->params[ctx->id("LUT1_INITVAL")] = str_or_default(ccu->params, ctx->id("INIT1"), "0"); diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 7a7e0dda..2f84cd02 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -532,6 +532,21 @@ class Ecp5Packer cell_count++; } } + + // Relative chain placement + for (auto &chain : all_chains) { + chain.cells.at(0)->constr_abs_z = true; + chain.cells.at(0)->constr_z = 0; + for (int i = 1; i < int(chain.cells.size()); i++) { + chain.cells.at(i)->constr_x = (i / 4); + chain.cells.at(i)->constr_y = 0; + chain.cells.at(i)->constr_z = i % 4; + chain.cells.at(i)->constr_abs_z = true; + chain.cells.at(i)->constr_parent = chain.cells.at(0); + chain.cells.at(0)->constr_children.push_back(chain.cells.at(i)); + } + } + flush_cells(); } @@ -650,6 +665,10 @@ class Ecp5Packer } else if (is_ff(ctx, uc) && user.port == ctx->id("CE")) { uc->params[ctx->id("CEMUX")] = constval ? "1" : "0"; uc->ports[user.port].net = nullptr; + } else if (is_carry(ctx, uc) && constval && + (user.port == id_A0 || user.port == id_A1 || user.port == id_B0 || user.port == id_B1 || + user.port == id_C0 || user.port == id_C1 || user.port == id_D0 || user.port == id_D1)) { + uc->ports[user.port].net = nullptr; } else if (is_ff(ctx, uc) && user.port == ctx->id("LSR") && ((!constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "LSR") || (constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "INV"))) { @@ -724,6 +743,7 @@ class Ecp5Packer { pack_io(); pack_constants(); + pack_carries(); find_lutff_pairs(); pack_lut5s(); pair_luts(); -- cgit v1.2.3 From 3e399c9f20f4caaab6cdad41cf4b66994be3d966 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 30 Sep 2018 18:10:20 +0100 Subject: ecp5: Carry packing fixes Signed-off-by: David Shah --- ecp5/pack.cc | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 2f84cd02..ecd2a058 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -121,8 +121,8 @@ class Ecp5Packer if (str_or_default(existing->params, ctx->id("LSRMUX"), "LSR") != str_or_default(ff0->params, ctx->id("LSRMUX"), "LSR")) return false; - if (str_or_default(existing->params, ctx->id("LSRMUX"), "LSR") != - str_or_default(ff0->params, ctx->id("LSRMUX"), "LSR")) + if (str_or_default(existing->params, ctx->id("SRMODE"), "LSR_OVER_CE") != + str_or_default(ff0->params, ctx->id("SRMODE"), "LSR_OVER_CE")) return false; } return true; @@ -354,6 +354,7 @@ class Ecp5Packer std::unique_ptr new_carry(new NetInfo()); new_carry->name = ctx->id(feedin->name.str(ctx) + "$COUT"); connect_port(ctx, new_carry.get(), feedin.get(), ctx->id("COUT")); + chain_in.cell->ports[chain_in.port].net = nullptr; connect_port(ctx, new_carry.get(), chain_in.cell, chain_in.port); CellInfo *feedin_ptr = feedin.get(); @@ -492,10 +493,13 @@ class Ecp5Packer } } + std::vector> packed_chains; + // Chain packing for (auto &chain : all_chains) { int cell_count = 0; std::vector tile_ffs; + std::vector packed_chain; for (auto &cell : chain.cells) { if (cell_count % 4 == 0) tile_ffs.clear(); @@ -526,24 +530,25 @@ class Ecp5Packer packed_cells.insert(ff1->name); } } - + packed_chain.push_back(slice.get()); new_cells.push_back(std::move(slice)); packed_cells.insert(cell->name); cell_count++; } + packed_chains.push_back(packed_chain); } // Relative chain placement - for (auto &chain : all_chains) { - chain.cells.at(0)->constr_abs_z = true; - chain.cells.at(0)->constr_z = 0; - for (int i = 1; i < int(chain.cells.size()); i++) { - chain.cells.at(i)->constr_x = (i / 4); - chain.cells.at(i)->constr_y = 0; - chain.cells.at(i)->constr_z = i % 4; - chain.cells.at(i)->constr_abs_z = true; - chain.cells.at(i)->constr_parent = chain.cells.at(0); - chain.cells.at(0)->constr_children.push_back(chain.cells.at(i)); + for (auto &chain : packed_chains) { + chain.at(0)->constr_abs_z = true; + chain.at(0)->constr_z = 0; + for (int i = 1; i < int(chain.size()); i++) { + chain.at(i)->constr_x = (i / 4); + chain.at(i)->constr_y = 0; + chain.at(i)->constr_z = i % 4; + chain.at(i)->constr_abs_z = true; + chain.at(i)->constr_parent = chain.at(0); + chain.at(0)->constr_children.push_back(chain.at(i)); } } -- cgit v1.2.3 From 6a1b49c3117da76c752d1d4e2fd1c1ed7eb94698 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 30 Sep 2018 18:39:49 +0100 Subject: ecp5: Improve mixed no-FF/FF placement Signed-off-by: David Shah --- ecp5/arch_place.cc | 38 ++++++++++++++++++++------------------ ecp5/archdefs.h | 1 + ecp5/bitstream.cc | 27 ++++++++++++++++----------- ecp5/pack.cc | 9 ++++++++- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc index 55fff73d..6fcd8bde 100644 --- a/ecp5/arch_place.cc +++ b/ecp5/arch_place.cc @@ -39,25 +39,27 @@ bool Arch::slicesCompatible(const std::vector &cells) const IdString CLKMUX, LSRMUX, SRMODE; bool first = true; for (auto cell : cells) { - if (first) { - clk_sig = cell->sliceInfo.clk_sig; - lsr_sig = cell->sliceInfo.lsr_sig; - CLKMUX = cell->sliceInfo.clkmux; - LSRMUX = cell->sliceInfo.lsrmux; - SRMODE = cell->sliceInfo.srmode; - } else { - if (cell->sliceInfo.clk_sig != clk_sig) - return false; - if (cell->sliceInfo.lsr_sig != lsr_sig) - return false; - if (cell->sliceInfo.clkmux != CLKMUX) - return false; - if (cell->sliceInfo.lsrmux != LSRMUX) - return false; - if (cell->sliceInfo.srmode != SRMODE) - return false; + if (cell->sliceInfo.using_dff) { + if (first) { + clk_sig = cell->sliceInfo.clk_sig; + lsr_sig = cell->sliceInfo.lsr_sig; + CLKMUX = cell->sliceInfo.clkmux; + LSRMUX = cell->sliceInfo.lsrmux; + SRMODE = cell->sliceInfo.srmode; + } else { + if (cell->sliceInfo.clk_sig != clk_sig) + return false; + if (cell->sliceInfo.lsr_sig != lsr_sig) + return false; + if (cell->sliceInfo.clkmux != CLKMUX) + return false; + if (cell->sliceInfo.lsrmux != LSRMUX) + return false; + if (cell->sliceInfo.srmode != SRMODE) + return false; + } + first = false; } - first = false; } return true; } diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index b5cdea3c..b85852c2 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -143,6 +143,7 @@ struct ArchCellInfo { struct { + bool using_dff; IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode; } sliceInfo; }; diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 5e851fbf..bfd51666 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -244,17 +244,22 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex cc.tiles[tname].add_enum(slice + ".REG1.REGSET", str_or_default(ci->params, ctx->id("REG1_REGSET"), "RESET")); cc.tiles[tname].add_enum(slice + ".CEMUX", str_or_default(ci->params, ctx->id("CEMUX"), "1")); - NetInfo *lsrnet = nullptr; - if (ci->ports.find(ctx->id("LSR")) != ci->ports.end() && ci->ports.at(ctx->id("LSR")).net != nullptr) - lsrnet = ci->ports.at(ctx->id("LSR")).net; - if (ctx->getBoundWireNet(ctx->getWireByName( - ctx->id(fmt_str("X" << bel.location.x << "/Y" << bel.location.y << "/LSR0")))) == lsrnet) { - cc.tiles[tname].add_enum("LSR0.SRMODE", str_or_default(ci->params, ctx->id("SRMODE"), "LSR_OVER_CE")); - cc.tiles[tname].add_enum("LSR0.LSRMUX", str_or_default(ci->params, ctx->id("LSRMUX"), "LSR")); - } else if (ctx->getBoundWireNet(ctx->getWireByName(ctx->id( - fmt_str("X" << bel.location.x << "/Y" << bel.location.y << "/LSR1")))) == lsrnet) { - cc.tiles[tname].add_enum("LSR1.SRMODE", str_or_default(ci->params, ctx->id("SRMODE"), "LSR_OVER_CE")); - cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, ctx->id("LSRMUX"), "LSR")); + + if (ci->sliceInfo.using_dff) { + NetInfo *lsrnet = nullptr; + if (ci->ports.find(ctx->id("LSR")) != ci->ports.end() && ci->ports.at(ctx->id("LSR")).net != nullptr) + lsrnet = ci->ports.at(ctx->id("LSR")).net; + if (ctx->getBoundWireNet(ctx->getWireByName( + ctx->id(fmt_str("X" << bel.location.x << "/Y" << bel.location.y << "/LSR0")))) == lsrnet) { + cc.tiles[tname].add_enum("LSR0.SRMODE", + str_or_default(ci->params, ctx->id("SRMODE"), "LSR_OVER_CE")); + cc.tiles[tname].add_enum("LSR0.LSRMUX", str_or_default(ci->params, ctx->id("LSRMUX"), "LSR")); + } else if (ctx->getBoundWireNet(ctx->getWireByName(ctx->id( + fmt_str("X" << bel.location.x << "/Y" << bel.location.y << "/LSR1")))) == lsrnet) { + cc.tiles[tname].add_enum("LSR1.SRMODE", + str_or_default(ci->params, ctx->id("SRMODE"), "LSR_OVER_CE")); + cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, ctx->id("LSRMUX"), "LSR")); + } } if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "CCU2") { diff --git a/ecp5/pack.cc b/ecp5/pack.cc index ecd2a058..23fd8f38 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -493,7 +493,7 @@ class Ecp5Packer } } - std::vector> packed_chains; + std::vector> packed_chains; // Chain packing for (auto &chain : all_chains) { @@ -797,6 +797,13 @@ void Arch::assignArchInfo() for (auto cell : sorted(cells)) { CellInfo *ci = cell.second; if (ci->type == id_TRELLIS_SLICE) { + + ci->sliceInfo.using_dff = false; + if (ci->ports.count(id_Q0) && ci->ports[id_Q0].net != nullptr) + ci->sliceInfo.using_dff = true; + if (ci->ports.count(id_Q1) && ci->ports[id_Q1].net != nullptr) + ci->sliceInfo.using_dff = true; + if (ci->ports.count(id_CLK) && ci->ports[id_CLK].net != nullptr) ci->sliceInfo.clk_sig = ci->ports[id_CLK].net->name; else -- cgit v1.2.3 From e7c881842470b8b94dd16e95db3015413cd499c3 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 30 Sep 2018 19:27:06 +0100 Subject: ecp5: Fix carry feed out Signed-off-by: David Shah --- ecp5/pack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 23fd8f38..d8db3b93 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -371,7 +371,7 @@ class Ecp5Packer std::unique_ptr feedout = create_ecp5_cell(ctx, ctx->id("CCU2C")); feedout->params[ctx->id("INIT0")] = "0"; feedout->params[ctx->id("INIT1")] = "10"; // LUT4 = 0; LUT2 = A - feedout->params[ctx->id("INJECT1_0")] = "YES"; + feedout->params[ctx->id("INJECT1_0")] = "NO"; feedout->params[ctx->id("INJECT1_1")] = "NO"; PortRef carry_drv = carry->driver; -- cgit v1.2.3 From 931c78b1bbb6acbc4f9c8058ed450bf9464cb603 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 1 Oct 2018 14:42:19 +0100 Subject: ecp5: Improve handling of constant CCU2C inputs Signed-off-by: David Shah --- ecp5/pack.cc | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/ecp5/pack.cc b/ecp5/pack.cc index d8db3b93..14b387d5 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -637,14 +637,12 @@ class Ecp5Packer flush_cells(); } - void set_lut_input_constant(CellInfo *cell, IdString input, bool value) + int make_init_with_const_input(int init, int input, bool value) { - int index = std::string("ABCD").find(input.str(ctx)); - int init = int_or_default(cell->params, ctx->id("INIT")); int new_init = 0; for (int i = 0; i < 16; i++) { - if (((i >> index) & 0x1) != value) { - int other_i = (i & (~(1 << index))) | (value << index); + if (((i >> input) & 0x1) != value) { + int other_i = (i & (~(1 << input))) | (value << input); if ((init >> other_i) & 0x1) new_init |= (1 << i); } else { @@ -652,10 +650,41 @@ class Ecp5Packer new_init |= (1 << i); } } + return new_init; + } + + void set_lut_input_constant(CellInfo *cell, IdString input, bool value) + { + int index = std::string("ABCD").find(input.str(ctx)); + int init = int_or_default(cell->params, ctx->id("INIT")); + int new_init = make_init_with_const_input(init, index, value); cell->params[ctx->id("INIT")] = std::to_string(new_init); cell->ports.at(input).net = nullptr; } + void set_ccu2c_input_constant(CellInfo *cell, IdString input, bool value) + { + std::string input_str = input.str(ctx); + int lut = std::stoi(input_str.substr(1)); + int index = std::string("ABCD").find(input_str[0]); + int init = int_or_default(cell->params, ctx->id("INIT" + std::to_string(lut))); + int new_init = make_init_with_const_input(init, index, value); + cell->params[ctx->id("INIT" + std::to_string(lut))] = std::to_string(new_init); + cell->ports.at(input).net = nullptr; + } + + bool is_ccu2c_port_high(CellInfo *cell, IdString input) + { + if (!cell->ports.count(input)) + return true; // disconnected port is high + if (cell->ports.at(input).net == nullptr || cell->ports.at(input).net->name == ctx->id("$PACKER_VCC_NET")) + return true; // disconnected or tied-high port + if (cell->ports.at(input).net->driver.cell != nullptr && + cell->ports.at(input).net->driver.cell->type == ctx->id("VCC")) + return true; // pre-pack high + return false; + } + // Merge a net into a constant net void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constnet, bool constval) { @@ -670,10 +699,37 @@ class Ecp5Packer } else if (is_ff(ctx, uc) && user.port == ctx->id("CE")) { uc->params[ctx->id("CEMUX")] = constval ? "1" : "0"; uc->ports[user.port].net = nullptr; - } else if (is_carry(ctx, uc) && constval && - (user.port == id_A0 || user.port == id_A1 || user.port == id_B0 || user.port == id_B1 || - user.port == id_C0 || user.port == id_C1 || user.port == id_D0 || user.port == id_D1)) { - uc->ports[user.port].net = nullptr; + } else if (is_carry(ctx, uc)) { + if (constval && + (user.port == id_A0 || user.port == id_A1 || user.port == id_B0 || user.port == id_B1 || + user.port == id_C0 || user.port == id_C1 || user.port == id_D0 || user.port == id_D1)) { + // Input tied high, nothing special to do (bitstream gen will auto-enable tie-high) + uc->ports[user.port].net = nullptr; + } else if (!constval) { + if (user.port == id_A0 || user.port == id_A1 || user.port == id_B0 || user.port == id_B1) { + // These inputs can be switched to tie-high without consequence + set_ccu2c_input_constant(uc, user.port, constval); + } else if (user.port == id_C0 && is_ccu2c_port_high(uc, id_D0)) { + // Partner must be tied high + set_ccu2c_input_constant(uc, user.port, constval); + } else if (user.port == id_D0 && is_ccu2c_port_high(uc, id_C0)) { + // Partner must be tied high + set_ccu2c_input_constant(uc, user.port, constval); + } else if (user.port == id_C1 && is_ccu2c_port_high(uc, id_D1)) { + // Partner must be tied high + set_ccu2c_input_constant(uc, user.port, constval); + } else if (user.port == id_D1 && is_ccu2c_port_high(uc, id_C1)) { + // Partner must be tied high + set_ccu2c_input_constant(uc, user.port, constval); + } else { + // Not allowed to change to a tie-high + uc->ports[user.port].net = constnet; + constnet->users.push_back(user); + } + } else { + uc->ports[user.port].net = constnet; + constnet->users.push_back(user); + } } else if (is_ff(ctx, uc) && user.port == ctx->id("LSR") && ((!constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "LSR") || (constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "INV"))) { -- cgit v1.2.3