From f4230553901de30f07be5906471b219ed255cb6e Mon Sep 17 00:00:00 2001 From: gatecat Date: Thu, 4 Aug 2022 09:04:02 +0200 Subject: fabulous: Add a viaduct uarch Signed-off-by: gatecat --- generic/viaduct/fabulous/validity_check.cc | 201 +++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 generic/viaduct/fabulous/validity_check.cc (limited to 'generic/viaduct/fabulous/validity_check.cc') diff --git a/generic/viaduct/fabulous/validity_check.cc b/generic/viaduct/fabulous/validity_check.cc new file mode 100644 index 00000000..91dba689 --- /dev/null +++ b/generic/viaduct/fabulous/validity_check.cc @@ -0,0 +1,201 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021-22 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 "validity_check.h" +#include "log.h" +#include "util.h" + +#define VIADUCT_CONSTIDS "viaduct/fabulous/constids.inc" +#include "viaduct_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +CLBState::CLBState(const LogicConfig &cfg) +{ + // TODO: more than one per LC if in split-SLICE mode with fracturable LUTs + lc_comb = std::make_unique(cfg.lc_per_clb); + if (cfg.split_lc) { + ff = std::make_unique(cfg.lc_per_clb * cfg.ff_per_lc); + } + // TODO: mux +} + +void CellTagger::assign_for(const Context *ctx, const FabricConfig &cfg, const CellInfo *ci) +{ + if (int(data.size()) <= ci->flat_index) + data.resize(ci->flat_index + 1); + auto &t = data.at(ci->flat_index); + // Use the same logic to handle both packed and split LC modes + if (ci->type.in(id_FABULOUS_COMB, id_FABULOUS_LC)) { + unsigned lut_input_count = 0; + for (unsigned i = 0; i < cfg.clb.lut_k; i++) + if (ci->getPort(ctx->idf("I%d", i))) + lut_input_count = i + 1; + t.comb.lut_inputs = SSOArray(lut_input_count, IdString()); + for (unsigned i = 0; i < lut_input_count; i++) { + const NetInfo *sig = ci->getPort(ctx->idf("I%d", i)); + t.comb.lut_inputs[i] = sig ? sig->name : IdString(); + } + t.comb.carry_used = false; // TODO + t.comb.lut_out = ci->getPort(id_O); + } + if (ci->type.in(id_FABULOUS_FF, id_FABULOUS_LC)) { + if (ci->type == id_FABULOUS_FF || bool_or_default(ci->params, id_FF)) { + t.ff.ff_used = true; + auto get_ctrlsig = [&](IdString name) { + const NetInfo *sig = ci->getPort(name); + bool invert = sig && bool_or_default(ci->params, ctx->idf("NEG_%s", name.c_str(ctx))); + return ControlSig(sig ? sig->name : id___disconnected, invert); + }; + t.ff.clk = get_ctrlsig(id_CLK); + t.ff.sr = get_ctrlsig(id_SR); + t.ff.en = get_ctrlsig(id_EN); + t.ff.async = bool_or_default(ci->params, id_ASYNC_SR); + t.ff.latch = bool_or_default(ci->params, id_LATCH_NOFF); + t.ff.d = ci->getPort(id_D); + t.ff.q = ci->getPort(id_Q); + } else { + t.ff.ff_used = false; + } + } +} + +void BlockTracker::set_bel_type(BelId bel, BelFlags::BlockType block, BelFlags::FuncType func, uint8_t index) +{ + Loc loc = ctx->getBelLocation(bel); + if (int(tiles.size()) <= loc.y) + tiles.resize(loc.y + 1); + auto &row = tiles.at(loc.y); + if (int(row.size()) <= loc.x) + row.resize(loc.x + 1); + auto &tile = row.at(loc.x); + if (block == BelFlags::BLOCK_CLB) { + if (!tile.clb) + tile.clb = std::make_unique(cfg.clb); + } + if (int(bel_data.size()) <= bel.index) + bel_data.resize(bel.index + 1); + auto &flags = bel_data.at(bel.index); + flags.block = block; + flags.func = func; + flags.index = index; +} + +void BlockTracker::update_bel(BelId bel, CellInfo *old_cell, CellInfo *new_cell) +{ + if (bel.index >= int(bel_data.size())) + return; // some kind of bel not being tracked + auto flags = bel_data.at(bel.index); + if (flags.block == BelFlags::BLOCK_OTHER) + return; // no structures to update + Loc loc = ctx->getBelLocation(bel); + if (loc.y >= int(tiles.size())) + return; // some kind of bel not being tracked + const auto &row = tiles.at(loc.y); + if (loc.x >= int(row.size())) + return; // some kind of bel not being tracked + const auto &entry = row.at(loc.x); + if (flags.block == BelFlags::BLOCK_CLB) { + NPNR_ASSERT(entry.clb); + // TODO: incremental validity check updates might care about this in the future, hence keeping it in the + // interface for now + NPNR_UNUSED(old_cell); + if (flags.func == BelFlags::FUNC_LC_COMB) + entry.clb->lc_comb[flags.index] = new_cell; + else if (flags.func == BelFlags::FUNC_FF) + entry.clb->ff[flags.index] = new_cell; + else if (flags.func == BelFlags::FUNC_MUX) + entry.clb->mux[flags.index] = new_cell; + } +} + +bool CLBState::check_validity(const LogicConfig &cfg, const CellTagger &cell_data) +{ + SSOArray used_clk(cfg.clk.routing.size()), used_sr(cfg.sr.routing.size()), + used_en(cfg.en.routing.size()); + auto check_ctrlsig = [&](unsigned idx, ControlSig actual, const ControlSetConfig &ctrl, + SSOArray &used) { + // see if we have an already-matching signal available + for (unsigned i = 0; i < ctrl.routing.size(); i++) { + // doesn't route to this pin + if (((ctrl.routing.at(i) >> unsigned(idx)) & 0x1U) == 0) + continue; + if (used[i] == actual) + return true; + } + // see if we have a free slot available + for (unsigned i = 0; i < ctrl.routing.size(); i++) { + // doesn't route to this pin + if (((ctrl.routing.at(i) >> unsigned(idx)) & 0x1U) == 0) + continue; + if (used[i] != ControlSig()) + continue; + used[i] = actual; + return true; + } + // no option available + return false; + }; + for (unsigned z = 0; z < cfg.lc_per_clb; z++) { + // flipflop control set checking + if (cfg.split_lc) { + NPNR_ASSERT_FALSE("unimplemented!"); // TODO + } else { + NPNR_ASSERT(cfg.ff_per_lc == 1); // split-slice mode must be used for more + const CellInfo *lc = lc_comb[z]; + if (!lc) + continue; + auto &lct = cell_data.get(lc); + if (lct.ff.ff_used) { + // check shared control signals + if (!check_ctrlsig(z, lct.ff.clk, cfg.clk, used_clk)) + return false; + if (cfg.en.have_signal && !check_ctrlsig(z, lct.ff.en, cfg.en, used_en)) + return false; + if (cfg.sr.have_signal && !check_ctrlsig(z, lct.ff.sr, cfg.sr, used_sr)) + return false; + } + } + } + // TODO: other checks... + return true; +} + +bool BlockTracker::check_validity(BelId bel, const FabricConfig &cfg, const CellTagger &cell_data) +{ + if (bel.index >= int(bel_data.size())) + return true; // some kind of bel not being tracked + auto flags = bel_data.at(bel.index); + if (flags.block == BelFlags::BLOCK_OTHER) + return true; // no structures to update + Loc loc = ctx->getBelLocation(bel); + if (loc.y >= int(tiles.size())) + return true; // some kind of bel not being tracked + const auto &row = tiles.at(loc.y); + if (loc.x >= int(row.size())) + return true; // some kind of bel not being tracked + const auto &entry = row.at(loc.x); + if (flags.block == BelFlags::BLOCK_CLB) { + return entry.clb->check_validity(cfg.clb, cell_data); + } else { + return true; + } +} + +NEXTPNR_NAMESPACE_END -- cgit v1.2.3