aboutsummaryrefslogtreecommitdiffstats
path: root/generic/viaduct/fabulous/validity_check.cc
diff options
context:
space:
mode:
authorgatecat <gatecat@ds0.me>2022-08-04 09:04:02 +0200
committergatecat <gatecat@ds0.me>2022-09-09 14:48:57 +0200
commitf4230553901de30f07be5906471b219ed255cb6e (patch)
tree601ada70dbe68cd9b34451840752868700de9612 /generic/viaduct/fabulous/validity_check.cc
parentb9b16eaa5358dbdf15368eb145be2c70b72c42a9 (diff)
downloadnextpnr-f4230553901de30f07be5906471b219ed255cb6e.tar.gz
nextpnr-f4230553901de30f07be5906471b219ed255cb6e.tar.bz2
nextpnr-f4230553901de30f07be5906471b219ed255cb6e.zip
fabulous: Add a viaduct uarch
Signed-off-by: gatecat <gatecat@ds0.me>
Diffstat (limited to 'generic/viaduct/fabulous/validity_check.cc')
-rw-r--r--generic/viaduct/fabulous/validity_check.cc201
1 files changed, 201 insertions, 0 deletions
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 <gatecat@ds0.me>
+ *
+ * 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<CellInfo *[]>(cfg.lc_per_clb);
+ if (cfg.split_lc) {
+ ff = std::make_unique<CellInfo *[]>(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<IdString, MAX_LUTK>(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<CLBState>(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<ControlSig, 2> 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<ControlSig, 2> &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