From c782f07b1bbb98d133cfa3c747fa591986abb1cb Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 31 Oct 2018 11:30:09 +0000 Subject: [PATCH] ecp5: Add IO buffer insertion Signed-off-by: David Shah --- ecp5/cells.cc | 42 ++++++++++++++++++++++++++++++++++++++++++ ecp5/cells.h | 4 ++++ ecp5/lpf.cc | 29 ++++++++++++++++------------- ecp5/pack.cc | 10 ++++++++-- 4 files changed, 70 insertions(+), 15 deletions(-) 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> &created_cells, + std::unordered_set &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 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> &created_cells, + std::unordered_set &todelete_cells); + NEXTPNR_NAMESPACE_END #endif diff --git a/ecp5/lpf.cc b/ecp5/lpf.cc index c8e61414..4bde660e 100644 --- a/ecp5/lpf.cc +++ b/ecp5/lpf.cc @@ -19,19 +19,22 @@ #include #include "log.h" -#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; - }}; + 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) @@ -50,24 +53,24 @@ bool Arch::applyLPF(std::string filename, std::istream &in) while (scpos != std::string::npos) { std::string command = linebuf.substr(0, scpos); // Split command into words - std::stringstream ss(line); + std::stringstream ss(command); std::vector 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") { + 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(1) == "SITE"); + 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(3)); + fnd_cell->second->attrs[id("LOC")] = strip_quotes(words.at(4)); } } else if (verb == "IOBUF") { NPNR_ASSERT(words.at(1) == "PORT"); @@ -87,7 +90,7 @@ bool Arch::applyLPF(std::string filename, std::istream &in) } } - linebuf = linebuf.substr(scpos+1); + linebuf = linebuf.substr(scpos + 1); scpos = linebuf.find(';'); } } 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 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()) {