From 153144022f12d2c4d9232a9d1107083c2f379121 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Thu, 30 Mar 2023 19:04:11 +0200 Subject: [PATCH] More of making it inline --- machxo2/arch.cc | 34 ++-- machxo2/arch_place.cc | 190 +++++++++++++++++++++++ machxo2/cells.cc | 41 ----- machxo2/cells.h | 37 ++--- machxo2/pack.cc | 349 ++++++++++++++++++++++-------------------- 5 files changed, 413 insertions(+), 238 deletions(-) create mode 100644 machxo2/arch_place.cc diff --git a/machxo2/arch.cc b/machxo2/arch.cc index be6d81a1..55d3a845 100644 --- a/machxo2/arch.cc +++ b/machxo2/arch.cc @@ -395,21 +395,33 @@ delay_t Arch::predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdStr bool Arch::place() { std::string placer = str_or_default(settings, id_placer, defaultPlacer); - if (placer == "sa") { - bool retVal = placer1(getCtx(), Placer1Cfg(getCtx())); - getCtx()->settings[id_place] = 1; - archInfoToAttributes(); - return retVal; - } else if (placer == "heap") { + + if (placer == "heap") { PlacerHeapCfg cfg(getCtx()); + cfg.criticalityExponent = 4; cfg.ioBufTypes.insert(id_TRELLIS_IO); - bool retVal = placer_heap(getCtx(), cfg); - getCtx()->settings[id_place] = 1; - archInfoToAttributes(); - return retVal; + + cfg.cellGroups.emplace_back(); + cfg.cellGroups.back().insert(id_TRELLIS_COMB); + cfg.cellGroups.back().insert(id_TRELLIS_FF); + cfg.cellGroups.back().insert(id_TRELLIS_RAMW); + cfg.placeAllAtOnce = true; + + cfg.beta = 0.75; + + if (!placer_heap(getCtx(), cfg)) + return false; + } else if (placer == "sa") { + if (!placer1(getCtx(), Placer1Cfg(getCtx()))) + return false; } else { log_error("MachXO2 architecture does not support placer '%s'\n", placer.c_str()); } + + getCtx()->settings[id_place] = 1; + + archInfoToAttributes(); + return true; } bool Arch::route() @@ -418,6 +430,8 @@ bool Arch::route() disable_router_lutperm = getCtx()->setting("arch.disable_router_lutperm", false); + assignArchInfo(); + bool result; if (router == "router1") { result = router1(getCtx(), Router1Cfg(getCtx())); diff --git a/machxo2/arch_place.cc b/machxo2/arch_place.cc new file mode 100644 index 00000000..1ffb9910 --- /dev/null +++ b/machxo2/arch_place.cc @@ -0,0 +1,190 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 gatecat + * + * 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 "cells.h" +#include "design_utils.h" +#include "log.h" +#include "nextpnr.h" +#include "timing.h" +#include "util.h" +NEXTPNR_NAMESPACE_BEGIN + +inline NetInfo *port_or_nullptr(const CellInfo *cell, IdString name) +{ + auto found = cell->ports.find(name); + if (found == cell->ports.end()) + return nullptr; + return found->second.net; +} + +bool Arch::slices_compatible(LogicTileStatus *lts) const +{ + if (lts == nullptr) + return true; + for (int sl = 0; sl < 4; sl++) { + if (!lts->slices[sl].dirty) { + if (!lts->slices[sl].valid) + return false; + continue; + } + lts->slices[sl].dirty = false; + lts->slices[sl].valid = false; + bool found_ff = false; + uint8_t last_ff_flags = 0; + IdString last_ce_sig; + bool ramw_used = false; + if (sl == 2 && lts->cells[((sl * 2) << lc_idx_shift) | BEL_RAMW] != nullptr) + ramw_used = true; + for (int l = 0; l < 2; l++) { + bool comb_m_used = false; + CellInfo *comb = lts->cells[((sl * 2 + l) << lc_idx_shift) | BEL_COMB]; + if (comb != nullptr) { + uint8_t flags = comb->combInfo.flags; + if (ramw_used && !(flags & ArchCellInfo::COMB_RAMW_BLOCK)) + return false; + if (flags & ArchCellInfo::COMB_MUX5) { + // MUX5 uses M signal and must be in LC 0 + comb_m_used = true; + if (l != 0) + return false; + } + if (flags & ArchCellInfo::COMB_MUX6) { + // MUX6+ uses M signal and must be in LC 1 + comb_m_used = true; + if (l != 1) + return false; + if (comb->combInfo.mux_fxad != nullptr && + (comb->combInfo.mux_fxad->combInfo.flags & ArchCellInfo::COMB_MUX5)) { + // LUT6 structure must be rooted at SLICE 0 or 2 + if (sl != 0 && sl != 2) + return false; + } + } + // LUTRAM must be in bottom two SLICEs only + if ((flags & ArchCellInfo::COMB_LUTRAM) && (sl > 1)) + return false; + if (l == 1) { + // Carry usage must be the same for LCs 0 and 1 in a SLICE + CellInfo *comb0 = lts->cells[((sl * 2 + 0) << lc_idx_shift) | BEL_COMB]; + if (comb0 && + ((comb0->combInfo.flags & ArchCellInfo::COMB_CARRY) != (flags & ArchCellInfo::COMB_CARRY))) + return false; + } + } + + CellInfo *ff = lts->cells[((sl * 2 + l) << lc_idx_shift) | BEL_FF]; + if (ff != nullptr) { + uint8_t flags = ff->ffInfo.flags; + if (comb_m_used && (flags & ArchCellInfo::FF_M_USED)) + return false; + if (found_ff) { + if ((flags & ArchCellInfo::FF_GSREN) != (last_ff_flags & ArchCellInfo::FF_GSREN)) + return false; + if ((flags & ArchCellInfo::FF_CECONST) != (last_ff_flags & ArchCellInfo::FF_CECONST)) + return false; + if ((flags & ArchCellInfo::FF_CEINV) != (last_ff_flags & ArchCellInfo::FF_CEINV)) + return false; + if (ff->ffInfo.ce_sig != last_ce_sig) + return false; + } else { + found_ff = true; + last_ff_flags = flags; + last_ce_sig = ff->ffInfo.ce_sig; + } + } + } + + lts->slices[sl].valid = true; + } + if (lts->tile_dirty) { + bool found_global_ff = false; + bool found_global_dpram = false; + bool global_lsrinv = false; + bool global_clkinv = false; + bool global_async = false; + + IdString clk_sig, lsr_sig; + + lts->tile_dirty = false; + lts->tile_valid = false; + +#define CHECK_EQUAL(x, y) \ + do { \ + if ((x) != (y)) \ + return false; \ + } while (0) + for (int i = 0; i < 8; i++) { + if (i < 4) { + // DPRAM + CellInfo *comb = lts->cells[(i << lc_idx_shift) | BEL_COMB]; + if (comb != nullptr && (comb->combInfo.flags & ArchCellInfo::COMB_LUTRAM)) { + if (found_global_dpram) { + CHECK_EQUAL(bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV), global_clkinv); + CHECK_EQUAL(bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WREINV), global_lsrinv); + } else { + global_clkinv = bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV); + global_lsrinv = bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WREINV); + found_global_dpram = true; + } + } + } + // FF + CellInfo *ff = lts->cells[(i << lc_idx_shift) | BEL_FF]; + if (ff != nullptr) { + if (found_global_dpram) { + CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV), global_clkinv); + CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV), global_lsrinv); + } + if (found_global_ff) { + CHECK_EQUAL(ff->ffInfo.clk_sig, clk_sig); + CHECK_EQUAL(ff->ffInfo.lsr_sig, lsr_sig); + CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV), global_clkinv); + CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV), global_lsrinv); + CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_ASYNC), global_async); + + } else { + clk_sig = ff->ffInfo.clk_sig; + lsr_sig = ff->ffInfo.lsr_sig; + global_clkinv = bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV); + global_lsrinv = bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV); + global_async = bool(ff->ffInfo.flags & ArchCellInfo::FF_ASYNC); + found_global_ff = true; + } + } + } +#undef CHECK_EQUAL + lts->tile_valid = true; + } else { + if (!lts->tile_valid) + return false; + } + + return true; +} + +bool Arch::isBelLocationValid(BelId bel, bool explain_invalid) const +{ + IdString bel_type = getBelType(bel); + if (bel_type.in(id_TRELLIS_COMB, id_TRELLIS_FF, id_TRELLIS_RAMW)) { + return slices_compatible(tile_status.at(tile_index(bel)).lts); + } + return true; +} + +NEXTPNR_NAMESPACE_END diff --git a/machxo2/cells.cc b/machxo2/cells.cc index edd9f816..22930297 100644 --- a/machxo2/cells.cc +++ b/machxo2/cells.cc @@ -249,47 +249,6 @@ void dram_to_comb(Context *ctx, CellInfo *ram, CellInfo *comb, CellInfo *ramw, i comb->attrs[attr.first] = attr.second; } -void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff) -{ - lc->params[id_LUT0_INITVAL] = lut->params[id_INIT]; - - for (std::string i : {"A", "B", "C", "D"}) { - IdString lut_port = ctx->id(i); - IdString lc_port = ctx->id(i + "0"); - lut->movePortTo(lut_port, lc, lc_port); - } - - lut->movePortTo(id_Z, lc, id_F0); -} - -void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, LutType lut_type) -{ - // FIXME: This will have to change once we support FFs with reset value of 1. - lc->params[id_REG0_REGSET] = std::string("RESET"); - - dff->movePortTo(id_CLK, lc, id_CLK); - dff->movePortTo(id_LSR, lc, id_LSR); - dff->movePortTo(id_Q, lc, id_Q0); - - if (lut_type == LutType::PassThru) { - // If a register's DI port is fed by a constant, options for placing are - // limited. Use the LUT to get around this. - // LUT output will go to F0, which will feed back to DI0 input. - lc->params[id_LUT0_INITVAL] = Property(0xAAAA, 16); - dff->movePortTo(id_DI, lc, id_A0); - lc->connectPorts(id_F0, lc, id_DI0); - } else if (lut_type == LutType::None) { - // If there is no LUT, use the M0 input because DI0 requires - // going through the LUTs. - lc->params[id_REG0_SD] = std::string("0"); - dff->movePortTo(id_DI, lc, id_M0); - } else { - // Otherwise, there's a LUT being used in the slice and mapping DI to - // DI0 input is fine. - dff->movePortTo(id_DI, lc, id_DI0); - } -} - void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector> &created_cells, pool &todelete_cells) { diff --git a/machxo2/cells.h b/machxo2/cells.h index cc64b52e..43411019 100644 --- a/machxo2/cells.h +++ b/machxo2/cells.h @@ -25,49 +25,36 @@ NEXTPNR_NAMESPACE_BEGIN -// When packing DFFs, we need context of how it's connected to a LUT to -// properly map DFF ports to TRELLIS_SLICEs; DI0 input muxes F0 and OFX0, -// and a DFF inside a slice can use either DI0 or M0 as an input. -enum class LutType -{ - None, - Normal, - PassThru, -}; - // Create a MachXO2 arch cell and return it // Name will be automatically assigned if not specified std::unique_ptr create_machxo2_cell(Context *ctx, IdString type, std::string name = ""); // Return true if a cell is a LUT inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_LUT4; } -inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_CCU2C; } -inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_DPR16X4; } -inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_IO; } // Return true if a cell is a flipflop inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_FF; } -// Convert a LUT primitive to (part of) an GENERIC_SLICE, swapping ports -// as needed. Set no_dff if a DFF is not being used, so that the output -// can be reconnected -void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff = true); +inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_CCU2C; } -// Convert a DFF primitive to (part of) an GENERIC_SLICE, setting parameters -// and reconnecting signals as necessary. If pass_thru_lut is True, the LUT will -// be configured as pass through and D connected to I0, otherwise D will be -// ignored -void dff_to_lc(Context *ctx, CellInfo *dff, CellInfo *lc, LutType lut_type = LutType::Normal); +inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_IO; } -// Convert a nextpnr IO buffer to a TRELLIS_IO -void nxio_to_tr(Context *ctx, CellInfo *nxio, CellInfo *trio, std::vector> &created_cells, - pool &todelete_cells); +inline bool is_dpram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_TRELLIS_DPR16X4; } + +inline bool is_pfumx(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_PFUMX; } + +inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_L6MUX21; } void lut_to_comb(Context *ctx, CellInfo *lut); void dram_to_ramw_split(Context *ctx, CellInfo *ram, CellInfo *ramw); void ccu2_to_comb(Context *ctx, CellInfo *ccu, CellInfo *comb, NetInfo *internal_carry, int i); void dram_to_comb(Context *ctx, CellInfo *ram, CellInfo *comb, 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, + pool &todelete_cells); + + NEXTPNR_NAMESPACE_END diff --git a/machxo2/pack.cc b/machxo2/pack.cc index 78becc18..e0ea62ea 100644 --- a/machxo2/pack.cc +++ b/machxo2/pack.cc @@ -303,6 +303,192 @@ class Ecp5Packer return false; } + // Pass to pack LUT5s into a newly created slice + void pack_lut5xs() + { + log_info("Packing LUT5-7s...\n"); + + // Gets the "COMB1" side of a LUT5, where we pack a LUT[67] into + auto get_comb1_from_lut5 = [&](CellInfo *lut5) { + NetInfo *f1 = lut5->getPort(id_F1); + NPNR_ASSERT(f1 != nullptr); + NPNR_ASSERT(f1->driver.cell != nullptr); + return f1->driver.cell; + }; + + dict> lut5_roots, lut6_roots, lut7_roots; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (is_pfumx(ctx, ci)) { + NetInfo *f0 = ci->ports.at(id_BLUT).net; + + if (f0 == nullptr) + log_error("PFUMX '%s' has disconnected port 'BLUT'\n", ci->name.c_str(ctx)); + NetInfo *f1 = ci->ports.at(id_ALUT).net; + if (f1 == nullptr) + log_error("PFUMX '%s' has disconnected port 'ALUT'\n", ci->name.c_str(ctx)); + + CellInfo *lut0 = + (f0->driver.cell && f0->driver.cell->type == id_TRELLIS_COMB && f0->driver.port == id_F) + ? f0->driver.cell + : nullptr; + CellInfo *lut1 = + (f1->driver.cell && f1->driver.cell->type == id_TRELLIS_COMB && f1->driver.port == id_F) + ? f1->driver.cell + : nullptr; + if (lut0 == nullptr || lut0->cluster != ClusterId()) + log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); + if (lut1 == nullptr || lut1->cluster != ClusterId()) + log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx)); + lut0->addInput(id_F1); + lut0->addInput(id_M); + lut0->addOutput(id_OFX); + + ci->movePortTo(id_Z, lut0, id_OFX); + ci->movePortTo(id_ALUT, lut0, id_F1); + ci->movePortTo(id_C0, lut0, id_M); + ci->disconnectPort(id_BLUT); + + lut5_roots[lut0->name] = {lut0, lut1}; + packed_cells.insert(ci->name); + } + } + flush_cells(); + // Pack LUT6s + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (is_l6mux(ctx, ci)) { + NetInfo *ofx0_0 = ci->ports.at(id_D0).net; + if (ofx0_0 == nullptr) + log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx)); + NetInfo *ofx0_1 = ci->ports.at(id_D1).net; + if (ofx0_1 == nullptr) + log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx)); + CellInfo *comb0 = (ofx0_0->driver.cell && ofx0_0->driver.cell->type == id_TRELLIS_COMB && + ofx0_0->driver.port == id_OFX) + ? ofx0_0->driver.cell + : nullptr; + CellInfo *comb1 = (ofx0_1->driver.cell && ofx0_1->driver.cell->type == id_TRELLIS_COMB && + ofx0_1->driver.port == id_OFX) + ? ofx0_1->driver.cell + : nullptr; + if (comb0 == nullptr) { + if (!net_driven_by(ctx, ofx0_0, is_l6mux, id_Z)) + log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX0 but not a LUT7 mux " + "('%s.%s')\n", + ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx), + ofx0_0->driver.port.c_str(ctx)); + continue; + } + if (lut6_roots.count(comb0->name)) + continue; + + if (comb1 == nullptr) { + if (!net_driven_by(ctx, ofx0_1, is_l6mux, id_Z)) + log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX0 but not a LUT7 mux " + "('%s.%s')\n", + ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx), + ofx0_0->driver.port.c_str(ctx)); + continue; + } + if (lut6_roots.count(comb1->name)) + continue; + if (ctx->verbose) + log_info(" mux '%s' forms part of a LUT6\n", cell.first.c_str(ctx)); + comb0 = get_comb1_from_lut5(comb0); + comb1 = get_comb1_from_lut5(comb1); + + comb1->addInput(id_FXA); + comb1->addInput(id_FXB); + comb1->addInput(id_M); + comb1->addOutput(id_OFX); + ci->movePortTo(id_D0, comb1, id_FXA); + ci->movePortTo(id_D1, comb1, id_FXB); + ci->movePortTo(id_SD, comb1, id_M); + ci->movePortTo(id_Z, comb1, id_OFX); + lut6_roots[comb1->name] = {comb0, comb1}; + packed_cells.insert(ci->name); + } + } + flush_cells(); + // Pack LUT7s + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (is_l6mux(ctx, ci)) { + NetInfo *ofx1_0 = ci->ports.at(id_D0).net; + if (ofx1_0 == nullptr) + log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx)); + NetInfo *ofx1_1 = ci->ports.at(id_D1).net; + if (ofx1_1 == nullptr) + log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx)); + CellInfo *comb1 = (ofx1_0->driver.cell && ofx1_0->driver.cell->type == id_TRELLIS_COMB && + ofx1_0->driver.port == id_OFX) + ? ofx1_0->driver.cell + : nullptr; + CellInfo *comb3 = (ofx1_1->driver.cell && ofx1_1->driver.cell->type == id_TRELLIS_COMB && + ofx1_1->driver.port == id_OFX) + ? ofx1_1->driver.cell + : nullptr; + if (comb1 == nullptr) + log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX ('%s.%s')\n", + ci->name.c_str(ctx), ofx1_0->driver.cell->name.c_str(ctx), + ofx1_0->driver.port.c_str(ctx)); + if (comb3 == nullptr) + log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX ('%s.%s')\n", + ci->name.c_str(ctx), ofx1_1->driver.cell->name.c_str(ctx), + ofx1_1->driver.port.c_str(ctx)); + + NetInfo *fxa_0 = comb1->ports.at(id_FXA).net; + if (fxa_0 == nullptr) + log_error("SLICE '%s' has disconnected port 'FXA'\n", comb1->name.c_str(ctx)); + NetInfo *fxa_1 = comb3->ports.at(id_FXA).net; + if (fxa_1 == nullptr) + log_error("SLICE '%s' has disconnected port 'FXA'\n", comb3->name.c_str(ctx)); + + CellInfo *comb2 = net_driven_by( + ctx, fxa_1, + [](const Context *ctx, const CellInfo *ci) { + (void)ctx; + return ci->type == id_TRELLIS_COMB; + }, + id_OFX); + if (comb2 == nullptr) + log_error("SLICE '%s' has FXA driven by cell other than a SLICE OFX0 ('%s.%s')\n", + comb3->name.c_str(ctx), fxa_1->driver.cell->name.c_str(ctx), + fxa_1->driver.port.c_str(ctx)); + comb2 = get_comb1_from_lut5(comb2); + comb2->addInput(id_FXA); + comb2->addInput(id_FXB); + comb2->addInput(id_M); + comb2->addOutput(id_OFX); + ci->movePortTo(id_D0, comb2, id_FXA); + ci->movePortTo(id_D1, comb2, id_FXB); + ci->movePortTo(id_SD, comb2, id_M); + ci->movePortTo(id_Z, comb2, id_OFX); + + lut7_roots[comb2->name] = {comb1, comb3}; + packed_cells.insert(ci->name); + } + } + + for (auto &root : lut7_roots) { + auto &cells = root.second; + cells.second->cluster = cells.second->name; + cells.second->constr_abs_z = true; + cells.second->constr_z = (1 << Arch::lc_idx_shift) | Arch::BEL_COMB; + rel_constr_cells(cells.second, cells.first, (4 << Arch::lc_idx_shift)); + } + for (auto &root : lut6_roots) { + auto &cells = root.second; + rel_constr_cells(cells.second, cells.first, (2 << Arch::lc_idx_shift)); + } + for (auto &root : lut5_roots) { + auto &cells = root.second; + rel_constr_cells(cells.first, cells.second, (1 << Arch::lc_idx_shift)); + } + flush_cells(); + } + // Simple "packer" to remove nextpnr IOBUFs, this assumes IOBUFs are manually instantiated void pack_io() { @@ -914,6 +1100,7 @@ class Ecp5Packer pack_dram(); pack_carries(); pack_luts(); + pack_lut5xs(); pack_ffs(); ctx->fixupHierarchy(); ctx->check(); @@ -1021,166 +1208,4 @@ void Arch::assignArchInfo() } } -inline NetInfo *port_or_nullptr(const CellInfo *cell, IdString name) -{ - auto found = cell->ports.find(name); - if (found == cell->ports.end()) - return nullptr; - return found->second.net; -} - -bool Arch::slices_compatible(LogicTileStatus *lts) const -{ - if (lts == nullptr) - return true; - for (int sl = 0; sl < 4; sl++) { - if (!lts->slices[sl].dirty) { - if (!lts->slices[sl].valid) - return false; - continue; - } - lts->slices[sl].dirty = false; - lts->slices[sl].valid = false; - bool found_ff = false; - uint8_t last_ff_flags = 0; - IdString last_ce_sig; - bool ramw_used = false; - if (sl == 2 && lts->cells[((sl * 2) << lc_idx_shift) | BEL_RAMW] != nullptr) - ramw_used = true; - for (int l = 0; l < 2; l++) { - bool comb_m_used = false; - CellInfo *comb = lts->cells[((sl * 2 + l) << lc_idx_shift) | BEL_COMB]; - if (comb != nullptr) { - uint8_t flags = comb->combInfo.flags; - if (ramw_used && !(flags & ArchCellInfo::COMB_RAMW_BLOCK)) - return false; - if (flags & ArchCellInfo::COMB_MUX5) { - // MUX5 uses M signal and must be in LC 0 - comb_m_used = true; - if (l != 0) - return false; - } - if (flags & ArchCellInfo::COMB_MUX6) { - // MUX6+ uses M signal and must be in LC 1 - comb_m_used = true; - if (l != 1) - return false; - if (comb->combInfo.mux_fxad != nullptr && - (comb->combInfo.mux_fxad->combInfo.flags & ArchCellInfo::COMB_MUX5)) { - // LUT6 structure must be rooted at SLICE 0 or 2 - if (sl != 0 && sl != 2) - return false; - } - } - // LUTRAM must be in bottom two SLICEs only - if ((flags & ArchCellInfo::COMB_LUTRAM) && (sl > 1)) - return false; - if (l == 1) { - // Carry usage must be the same for LCs 0 and 1 in a SLICE - CellInfo *comb0 = lts->cells[((sl * 2 + 0) << lc_idx_shift) | BEL_COMB]; - if (comb0 && - ((comb0->combInfo.flags & ArchCellInfo::COMB_CARRY) != (flags & ArchCellInfo::COMB_CARRY))) - return false; - } - } - - CellInfo *ff = lts->cells[((sl * 2 + l) << lc_idx_shift) | BEL_FF]; - if (ff != nullptr) { - uint8_t flags = ff->ffInfo.flags; - if (comb_m_used && (flags & ArchCellInfo::FF_M_USED)) - return false; - if (found_ff) { - if ((flags & ArchCellInfo::FF_GSREN) != (last_ff_flags & ArchCellInfo::FF_GSREN)) - return false; - if ((flags & ArchCellInfo::FF_CECONST) != (last_ff_flags & ArchCellInfo::FF_CECONST)) - return false; - if ((flags & ArchCellInfo::FF_CEINV) != (last_ff_flags & ArchCellInfo::FF_CEINV)) - return false; - if (ff->ffInfo.ce_sig != last_ce_sig) - return false; - } else { - found_ff = true; - last_ff_flags = flags; - last_ce_sig = ff->ffInfo.ce_sig; - } - } - } - - lts->slices[sl].valid = true; - } - if (lts->tile_dirty) { - bool found_global_ff = false; - bool found_global_dpram = false; - bool global_lsrinv = false; - bool global_clkinv = false; - bool global_async = false; - - IdString clk_sig, lsr_sig; - - lts->tile_dirty = false; - lts->tile_valid = false; - -#define CHECK_EQUAL(x, y) \ - do { \ - if ((x) != (y)) \ - return false; \ - } while (0) - for (int i = 0; i < 8; i++) { - if (i < 4) { - // DPRAM - CellInfo *comb = lts->cells[(i << lc_idx_shift) | BEL_COMB]; - if (comb != nullptr && (comb->combInfo.flags & ArchCellInfo::COMB_LUTRAM)) { - if (found_global_dpram) { - CHECK_EQUAL(bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV), global_clkinv); - CHECK_EQUAL(bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WREINV), global_lsrinv); - } else { - global_clkinv = bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WCKINV); - global_lsrinv = bool(comb->combInfo.flags & ArchCellInfo::COMB_RAM_WREINV); - found_global_dpram = true; - } - } - } - // FF - CellInfo *ff = lts->cells[(i << lc_idx_shift) | BEL_FF]; - if (ff != nullptr) { - if (found_global_dpram) { - CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV), global_clkinv); - CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV), global_lsrinv); - } - if (found_global_ff) { - CHECK_EQUAL(ff->ffInfo.clk_sig, clk_sig); - CHECK_EQUAL(ff->ffInfo.lsr_sig, lsr_sig); - CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV), global_clkinv); - CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV), global_lsrinv); - CHECK_EQUAL(bool(ff->ffInfo.flags & ArchCellInfo::FF_ASYNC), global_async); - - } else { - clk_sig = ff->ffInfo.clk_sig; - lsr_sig = ff->ffInfo.lsr_sig; - global_clkinv = bool(ff->ffInfo.flags & ArchCellInfo::FF_CLKINV); - global_lsrinv = bool(ff->ffInfo.flags & ArchCellInfo::FF_LSRINV); - global_async = bool(ff->ffInfo.flags & ArchCellInfo::FF_ASYNC); - found_global_ff = true; - } - } - } -#undef CHECK_EQUAL - lts->tile_valid = true; - } else { - if (!lts->tile_valid) - return false; - } - - return true; -} - -bool Arch::isBelLocationValid(BelId bel, bool explain_invalid) const -{ - IdString bel_type = getBelType(bel); - if (bel_type.in(id_TRELLIS_COMB, id_TRELLIS_FF, id_TRELLIS_RAMW)) { - return slices_compatible(tile_status.at(tile_index(bel)).lts); - } - return true; -} - NEXTPNR_NAMESPACE_END