From 661237eb64a694a11900f736b07132ef8da4b0dd Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 22 Jun 2019 16:57:00 +0100 Subject: ecp5: Add --out-of-context for building hard macros Signed-off-by: David Shah --- ecp5/arch.cc | 8 +++++++- ecp5/arch_place.cc | 5 ++++- ecp5/bitstream.cc | 10 ++++------ ecp5/cells.cc | 4 ++-- ecp5/globals.cc | 6 +++++- ecp5/main.cc | 7 +++++++ ecp5/pack.cc | 8 +++++++- 7 files changed, 36 insertions(+), 12 deletions(-) (limited to 'ecp5') diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 4be9833e..9f137b9b 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -524,9 +524,15 @@ bool Arch::place() } else { log_error("ECP5 architecture does not support placer '%s'\n", placer.c_str()); } - permute_luts(); + + // In out-of-context mode, create a locked macro + if (bool_or_default(settings, id("arch.ooc"))) + for (auto &cell : cells) + cell.second->belStrength = STRENGTH_LOCKED; + getCtx()->settings[getCtx()->id("place")] = 1; + archInfoToAttributes(); return true; } diff --git a/ecp5/arch_place.cc b/ecp5/arch_place.cc index b5c11851..18374c07 100644 --- a/ecp5/arch_place.cc +++ b/ecp5/arch_place.cc @@ -152,7 +152,10 @@ void Arch::permute_luts() inputs.emplace_back(crit, i); } // Least critical first (A input is slowest) - std::sort(inputs.begin(), inputs.end()); + + // Avoid permuting locked LUTs (e.g. from an OOC submodule) + if (ci->belStrength <= STRENGTH_STRONG) + std::sort(inputs.begin(), inputs.end()); for (int i = 0; i < 4; i++) { IdString p = port_names.at(i); // log_info("%s %s %f\n", p.c_str(ctx), port_names.at(inputs.at(i).second).c_str(ctx), inputs.at(i).first); diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index a4b345e6..1e0dcadc 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -789,15 +789,13 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex 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")); + str_or_default(ci->params, ctx->id("CCU2_INJECT1_0"), "YES")); cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1", - str_or_default(ci->params, ctx->id("INJECT1_1"), "YES")); + str_or_default(ci->params, ctx->id("CCU2_INJECT1_1"), "YES")); } else { // Don't interfere with cascade mux wiring - cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0", - str_or_default(ci->params, ctx->id("INJECT1_0"), "_NONE_")); - cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1", - str_or_default(ci->params, ctx->id("INJECT1_1"), "_NONE_")); + cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0", "_NONE_"); + cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1", "_NONE_"); } if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "DPRAM" && slice == "SLICEA") { diff --git a/ecp5/cells.cc b/ecp5/cells.cc index 46f84d97..37b6ac8b 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -274,8 +274,8 @@ void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc) lc->params[ctx->id("LUT0_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT0"), Property(0, 16)); lc->params[ctx->id("LUT1_INITVAL")] = get_or_default(ccu->params, ctx->id("INIT1"), Property(0, 16)); - 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"); + lc->params[ctx->id("CCU2_INJECT1_0")] = str_or_default(ccu->params, ctx->id("INJECT1_0"), "YES"); + lc->params[ctx->id("CCU2_INJECT1_1")] = str_or_default(ccu->params, ctx->id("INJECT1_1"), "YES"); replace_port(ccu, ctx->id("CIN"), lc, ctx->id("FCI")); diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 5fb348e7..9dd4449b 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -431,11 +431,15 @@ class Ecp5GlobalRouter public: void promote_globals() { + bool is_ooc = bool_or_default(ctx->settings, ctx->id("arch.ooc")); log_info("Promoting globals...\n"); auto clocks = get_clocks(); for (auto clock : clocks) { log_info(" promoting clock net %s to global network\n", clock->name.c_str(ctx)); - insert_dcc(clock); + if (is_ooc) // Don't actually do anything in OOC mode, global routing will be done in the full design + clock->is_global = true; + else + insert_dcc(clock); } } diff --git a/ecp5/main.cc b/ecp5/main.cc index 5544c55f..71dffc8d 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -71,6 +71,8 @@ po::options_description ECP5CommandHandler::getArchOptions() specific.add_options()("lpf", po::value>(), "LPF pin constraint file(s)"); specific.add_options()("lpf-allow-unconstrained", "don't require LPF file(s) to constrain all IO"); + specific.add_options()("out-of-context", "disable IO buffer insertion and global promotion/routing, for building pre-routed blocks (experimental)"); + return specific; } void ECP5CommandHandler::validate() @@ -91,6 +93,9 @@ void ECP5CommandHandler::customBitstream(Context *ctx) basecfg = vm["basecfg"].as(); } + if (bool_or_default(ctx->settings, ctx->id("arch.ooc")) && vm.count("textcfg")) + log_error("bitstream generation is not available in out-of-context mode (use --write to create a post-PnR JSON design)\n"); + std::string textcfg; if (vm.count("textcfg")) textcfg = vm["textcfg"].as(); @@ -228,6 +233,8 @@ std::unique_ptr ECP5CommandHandler::createContext(std::unordered_mapsettings[ctx->id(val.first)] = val.second; ctx->settings[ctx->id("arch.package")] = ctx->archArgs().package; ctx->settings[ctx->id("arch.speed")] = speedString(ctx->archArgs().speed); + if (vm.count("out-of-context")) + ctx->settings[ctx->id("arch.ooc")] = 1; return ctx; } diff --git a/ecp5/pack.cc b/ecp5/pack.cc index a9416f32..7cf9df78 100644 --- a/ecp5/pack.cc +++ b/ecp5/pack.cc @@ -356,7 +356,12 @@ class Ecp5Packer ionet = ci->ports.at(ctx->id("I")).net; trio = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_trellis_io, ctx->id("B"), true, ci); } - if (trio != nullptr) { + if (bool_or_default(ctx->settings, ctx->id("arch.ooc"))) { + // No IO buffer insertion in out-of-context mode, just remove the nextpnr buffer + // and leave the top level port + for (auto &port : ci->ports) + disconnect_port(ctx, ci, port.first); + } else if (trio != nullptr) { // Trivial case, TRELLIS_IO used. Just destroy the net and the // iobuf log_info("%s feeds TRELLIS_IO %s, removing %s %s.\n", ci->name.c_str(ctx), trio->name.c_str(ctx), @@ -673,6 +678,7 @@ class Ecp5Packer CellInfo *make_carry_feed_out(NetInfo *carry, boost::optional chain_next = boost::optional()) { std::unique_ptr feedout = create_ecp5_cell(ctx, ctx->id("CCU2C")); + feedout->params[ctx->id("INIT0")] = Property(0, 16); feedout->params[ctx->id("INIT1")] = Property(10, 16); // LUT4 = 0; LUT2 = A feedout->params[ctx->id("INJECT1_0")] = std::string("NO"); -- cgit v1.2.3