diff options
Diffstat (limited to 'ecp5')
-rw-r--r-- | ecp5/arch.h | 3 | ||||
-rw-r--r-- | ecp5/cells.cc | 42 | ||||
-rw-r--r-- | ecp5/cells.h | 4 | ||||
-rw-r--r-- | ecp5/lpf.cc | 106 | ||||
-rw-r--r-- | ecp5/main.cc | 16 | ||||
-rw-r--r-- | ecp5/pack.cc | 10 |
6 files changed, 179 insertions, 2 deletions
diff --git a/ecp5/arch.h b/ecp5/arch.h index 35c8df19..0a5de822 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -917,6 +917,9 @@ struct Arch : BaseCtx GlobalInfoPOD globalInfoAtLoc(Location loc); + // Apply LPF constraints to the context + bool applyLPF(std::string filename, std::istream &in); + IdString id_trellis_slice; IdString id_clk, id_lsr; IdString id_clkmux, id_lsrmux; diff --git a/ecp5/cells.cc b/ecp5/cells.cc index a728104d..31839ee4 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -343,4 +343,46 @@ void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw } } +void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::unique_ptr<CellInfo>> &created_cells, + std::unordered_set<IdString> &todelete_cells) +{ + if (nxio->type == ctx->id("$nextpnr_ibuf")) { + trio->params[ctx->id("DIR")] = "INPUT"; + replace_port(nxio, ctx->id("O"), trio, ctx->id("O")); + } else if (nxio->type == ctx->id("$nextpnr_obuf")) { + trio->params[ctx->id("DIR")] = "OUTPUT"; + replace_port(nxio, ctx->id("I"), trio, ctx->id("I")); + } else if (nxio->type == ctx->id("$nextpnr_iobuf")) { + // N.B. tristate will be dealt with below + trio->params[ctx->id("DIR")] = "BIDIR"; + replace_port(nxio, ctx->id("I"), trio, ctx->id("I")); + replace_port(nxio, ctx->id("O"), trio, ctx->id("O")); + } else { + NPNR_ASSERT(false); + } + NetInfo *donet = trio->ports.at(ctx->id("I")).net; + CellInfo *tbuf = net_driven_by( + ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); }, + ctx->id("Y")); + if (tbuf) { + replace_port(tbuf, ctx->id("I"), trio, ctx->id("I")); + // Need to invert E to form T + std::unique_ptr<CellInfo> inv_lut = create_ecp5_cell(ctx, ctx->id("LUT4"), trio->name.str(ctx) + "$invert_T"); + replace_port(tbuf, ctx->id("E"), inv_lut.get(), ctx->id("A")); + inv_lut->params[ctx->id("INIT")] = "21845"; + connect_ports(ctx, inv_lut.get(), ctx->id("Z"), trio, ctx->id("T")); + created_cells.push_back(std::move(inv_lut)); + + if (donet->users.size() > 1) { + for (auto user : donet->users) + log_info(" remaining tristate user: %s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx)); + log_error("unsupported tristate IO pattern for IO buffer '%s', " + "instantiate SB_IO manually to ensure correct behaviour\n", + nxio->name.c_str(ctx)); + } + ctx->nets.erase(donet->name); + todelete_cells.insert(tbuf->name); + } +} + NEXTPNR_NAMESPACE_END diff --git a/ecp5/cells.h b/ecp5/cells.h index a5229fe0..9c2ff3cf 100644 --- a/ecp5/cells.h +++ b/ecp5/cells.h @@ -52,6 +52,10 @@ void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc); void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc); void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index); +// Convert a nextpnr IO buffer to a TRELLIS_IO +void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector<std::unique_ptr<CellInfo>> &created_cells, + std::unordered_set<IdString> &todelete_cells); + NEXTPNR_NAMESPACE_END #endif diff --git a/ecp5/lpf.cc b/ecp5/lpf.cc new file mode 100644 index 00000000..4bde660e --- /dev/null +++ b/ecp5/lpf.cc @@ -0,0 +1,106 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah <david@symbioticeda.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 <sstream> +#include "log.h" + +NEXTPNR_NAMESPACE_BEGIN + +bool Arch::applyLPF(std::string filename, std::istream &in) +{ + auto isempty = [](const std::string &str) { + return std::all_of(str.begin(), str.end(), [](char c) { return isblank(c); }); + }; + auto strip_quotes = [](const std::string &str) { + if (str.at(0) == '"') { + NPNR_ASSERT(str.back() == '"'); + return str.substr(1, str.size() - 2); + } else { + return str; + } + }; + + try { + if (!in) + log_error("failed to open LPF file\n"); + std::string line; + std::string linebuf; + while (std::getline(in, line)) { + size_t cstart = line.find('#'); + if (cstart != std::string::npos) + line = line.substr(0, cstart); + if (isempty(line)) + continue; + linebuf += line; + // Look for a command up to a semicolon + size_t scpos = linebuf.find(';'); + while (scpos != std::string::npos) { + std::string command = linebuf.substr(0, scpos); + // Split command into words + std::stringstream ss(command); + std::vector<std::string> words; + std::string tmp; + while (ss >> tmp) + words.push_back(tmp); + if (words.size() >= 0) { + std::string verb = words.at(0); + if (verb == "BLOCK" || verb == "SYSCONFIG" || verb == "FREQUENCY") { + log_warning(" ignoring unsupported LPF command '%s'\n", command.c_str()); + } else if (verb == "LOCATE") { + NPNR_ASSERT(words.at(1) == "COMP"); + std::string cell = strip_quotes(words.at(2)); + NPNR_ASSERT(words.at(3) == "SITE"); + auto fnd_cell = cells.find(id(cell)); + if (fnd_cell == cells.end()) { + log_warning("unmatched LPF 'LOCATE COMP' '%s'\n", cell.c_str()); + } else { + fnd_cell->second->attrs[id("LOC")] = strip_quotes(words.at(4)); + } + } else if (verb == "IOBUF") { + NPNR_ASSERT(words.at(1) == "PORT"); + std::string cell = strip_quotes(words.at(2)); + auto fnd_cell = cells.find(id(cell)); + if (fnd_cell == cells.end()) { + log_warning("unmatched LPF 'IOBUF PORT' '%s'\n", cell.c_str()); + } else { + for (size_t i = 3; i < words.size(); i++) { + std::string setting = words.at(i); + size_t eqpos = setting.find('='); + NPNR_ASSERT(eqpos != std::string::npos); + std::string key = setting.substr(0, eqpos), value = setting.substr(eqpos + 1); + fnd_cell->second->attrs[id(key)] = value; + } + } + } + } + + linebuf = linebuf.substr(scpos + 1); + scpos = linebuf.find(';'); + } + } + if (!isempty(linebuf)) + log_error("unexpected end of LPF file\n"); + settings.emplace(id("input/lpf"), filename); + return true; + } catch (log_execution_error_exception) { + return false; + } +} + +NEXTPNR_NAMESPACE_END diff --git a/ecp5/main.cc b/ecp5/main.cc index e71b0983..c444f96f 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -35,6 +35,7 @@ class ECP5CommandHandler : public CommandHandler virtual ~ECP5CommandHandler(){}; std::unique_ptr<Context> createContext() override; void setupArchContext(Context *ctx) override{}; + void customAfterLoad(Context *ctx) override; void validate() override; void customBitstream(Context *ctx) override; @@ -56,9 +57,13 @@ po::options_description ECP5CommandHandler::getArchOptions() specific.add_options()("um5g-25k", "set device type to LFE5UM5G-25F"); specific.add_options()("um5g-45k", "set device type to LFE5UM5G-45F"); specific.add_options()("um5g-85k", "set device type to LFE5UM5G-85F"); + specific.add_options()("package", po::value<std::string>(), "select device package (defaults to CABGA381)"); specific.add_options()("basecfg", po::value<std::string>(), "base chip configuration in Trellis text format"); specific.add_options()("textcfg", po::value<std::string>(), "textual configuration in Trellis format to write"); + + specific.add_options()("lpf", po::value<std::vector<std::string>>(), "LPF pin constraint file(s)"); + return specific; } void ECP5CommandHandler::validate() @@ -111,6 +116,17 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext() return std::unique_ptr<Context>(new Context(chipArgs)); } +void ECP5CommandHandler::customAfterLoad(Context *ctx) +{ + if (vm.count("lpf")) { + std::vector<std::string> files = vm["lpf"].as<std::vector<std::string>>(); + for (const auto &filename : files) { + std::ifstream in(filename); + ctx->applyLPF(filename, in); + } + } +} + int main(int argc, char *argv[]) { ECP5CommandHandler handler(argc, argv); diff --git a/ecp5/pack.cc b/ecp5/pack.cc index 18debb74..cb111fc6 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -266,11 +266,17 @@ class Ecp5Packer } } } else { - log_error("TRELLIS_IO required on all top level IOs...\n"); + // Create a TRELLIS_IO buffer + std::unique_ptr<CellInfo> tr_cell = + create_ecp5_cell(ctx, ctx->id("TRELLIS_IO"), ci->name.str(ctx) + "$tr_io"); + nxio_to_tr(ctx, ci, tr_cell.get(), new_cells, packed_cells); + new_cells.push_back(std::move(tr_cell)); + trio = new_cells.back().get(); } packed_cells.insert(ci->name); - std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(trio->attrs, trio->attrs.begin())); + for (const auto &attr : ci->attrs) + trio->attrs[attr.first] = attr.second; auto loc_attr = trio->attrs.find(ctx->id("LOC")); if (loc_attr != trio->attrs.end()) { |