aboutsummaryrefslogtreecommitdiffstats
path: root/machxo2/arch.cc
diff options
context:
space:
mode:
Diffstat (limited to 'machxo2/arch.cc')
-rw-r--r--machxo2/arch.cc494
1 files changed, 494 insertions, 0 deletions
diff --git a/machxo2/arch.cc b/machxo2/arch.cc
new file mode 100644
index 00000000..2938f1ba
--- /dev/null
+++ b/machxo2/arch.cc
@@ -0,0 +1,494 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Claire Xen <claire@symbioticeda.com>
+ * Copyright (C) 2021 William D. Jones <wjones@wdj-consulting.com>
+ *
+ * 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 <iostream>
+#include <math.h>
+#include "embed.h"
+#include "nextpnr.h"
+#include "placer1.h"
+#include "placer_heap.h"
+#include "router1.h"
+#include "router2.h"
+#include "util.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+// -----------------------------------------------------------------------
+
+void IdString::initialize_arch(const BaseCtx *ctx)
+{
+#define X(t) initialize_add(ctx, #t, ID_##t);
+
+#include "constids.inc"
+
+#undef X
+}
+
+// ---------------------------------------------------------------
+
+static const ChipInfoPOD *get_chip_info(ArchArgs::ArchArgsTypes chip)
+{
+ std::string chipdb;
+ if (chip == ArchArgs::LCMXO2_256HC) {
+ chipdb = "machxo2/chipdb-256.bin";
+ } else if (chip == ArchArgs::LCMXO2_640HC) {
+ chipdb = "machxo2/chipdb-640.bin";
+ } else if (chip == ArchArgs::LCMXO2_1200HC) {
+ chipdb = "machxo2/chipdb-1200.bin";
+ } else if (chip == ArchArgs::LCMXO2_2000HC) {
+ chipdb = "machxo2/chipdb-2000.bin";
+ } else if (chip == ArchArgs::LCMXO2_4000HC) {
+ chipdb = "machxo2/chipdb-4000.bin";
+ } else if (chip == ArchArgs::LCMXO2_7000HC) {
+ chipdb = "machxo2/chipdb-7000.bin";
+ } else {
+ log_error("Unknown chip\n");
+ }
+
+ auto ptr = reinterpret_cast<const RelPtr<ChipInfoPOD> *>(get_chipdb(chipdb));
+ if (ptr == nullptr)
+ return nullptr;
+ return ptr->get();
+}
+
+// ---------------------------------------------------------------
+
+Arch::Arch(ArchArgs args) : args(args)
+{
+ chip_info = get_chip_info(args.type);
+ if (chip_info == nullptr)
+ log_error("Unsupported MachXO2 chip type.\n");
+ if (chip_info->const_id_count != DB_CONST_ID_COUNT)
+ log_error("Chip database 'bba' and nextpnr code are out of sync; please rebuild (or contact distribution "
+ "maintainer)!\n");
+
+ package_info = nullptr;
+ for (int i = 0; i < chip_info->num_packages; i++) {
+ if (args.package == chip_info->package_info[i].name.get()) {
+ package_info = &(chip_info->package_info[i]);
+ break;
+ }
+ }
+ if (!package_info)
+ log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str());
+
+ BaseArch::init_cell_types();
+ BaseArch::init_bel_buckets();
+
+ for (int i = 0; i < chip_info->width; i++)
+ x_ids.push_back(id(stringf("X%d", i)));
+ for (int i = 0; i < chip_info->height; i++)
+ y_ids.push_back(id(stringf("Y%d", i)));
+
+ for (int i = 0; i < chip_info->width; i++) {
+ IdString x_id = id(stringf("X%d", i));
+ x_ids.push_back(x_id);
+ id_to_x[x_id] = i;
+ }
+ for (int i = 0; i < chip_info->height; i++) {
+ IdString y_id = id(stringf("Y%d", i));
+ y_ids.push_back(y_id);
+ id_to_y[y_id] = i;
+ }
+}
+
+bool Arch::is_available(ArchArgs::ArchArgsTypes chip) { return get_chip_info(chip) != nullptr; }
+
+std::vector<std::string> Arch::get_supported_packages(ArchArgs::ArchArgsTypes chip)
+{
+ const ChipInfoPOD *chip_info = get_chip_info(chip);
+ std::vector<std::string> pkgs;
+ for (int i = 0; i < chip_info->num_packages; i++) {
+ pkgs.push_back(chip_info->package_info[i].name.get());
+ }
+ return pkgs;
+}
+
+std::string Arch::getChipName() const
+{
+ if (args.type == ArchArgs::LCMXO2_256HC) {
+ return "LCMXO2-256HC";
+ } else if (args.type == ArchArgs::LCMXO2_640HC) {
+ return "LCMXO2-640HC";
+ } else if (args.type == ArchArgs::LCMXO2_1200HC) {
+ return "LCMXO2-1200HC";
+ } else if (args.type == ArchArgs::LCMXO2_2000HC) {
+ return "LCMXO2-2000HC";
+ } else if (args.type == ArchArgs::LCMXO2_4000HC) {
+ return "LCMXO2-4000HC";
+ } else if (args.type == ArchArgs::LCMXO2_7000HC) {
+ return "LCMXO2-7000HC";
+ } else {
+ log_error("Unknown chip\n");
+ }
+}
+
+std::string Arch::get_full_chip_name() const
+{
+ std::string name = getChipName();
+ name += "-";
+ switch (args.speed) {
+ case ArchArgs::SPEED_1:
+ name += "1";
+ break;
+ case ArchArgs::SPEED_2:
+ name += "2";
+ break;
+ case ArchArgs::SPEED_3:
+ name += "3";
+ case ArchArgs::SPEED_4:
+ name += "4";
+ break;
+ case ArchArgs::SPEED_5:
+ name += "5";
+ break;
+ case ArchArgs::SPEED_6:
+ name += "6";
+ break;
+ }
+ name += args.package;
+ return name;
+}
+
+IdString Arch::archArgsToId(ArchArgs args) const
+{
+ if (args.type == ArchArgs::LCMXO2_256HC) {
+ return id("lcmxo2_256hc");
+ } else if (args.type == ArchArgs::LCMXO2_640HC) {
+ return id("lcmxo2_640hc");
+ } else if (args.type == ArchArgs::LCMXO2_1200HC) {
+ return id("lcmxo2_1200hc");
+ } else if (args.type == ArchArgs::LCMXO2_2000HC) {
+ return id("lcmxo2_2000hc");
+ } else if (args.type == ArchArgs::LCMXO2_4000HC) {
+ return id("lcmxo2_4000hc");
+ } else if (args.type == ArchArgs::LCMXO2_7000HC) {
+ return id("lcmxo2_7000hc");
+ }
+
+ return IdString();
+}
+
+// ---------------------------------------------------------------
+
+BelId Arch::getBelByName(IdStringList name) const
+{
+ if (name.size() != 3)
+ return BelId();
+ BelId ret;
+ Location loc;
+ loc.x = id_to_x.at(name[0]);
+ loc.y = id_to_y.at(name[1]);
+ ret.location = loc;
+ const TileTypePOD *loci = tile_info(ret);
+ for (int i = 0; i < loci->num_bels; i++) {
+ if (std::strcmp(loci->bel_data[i].name.get(), name[2].c_str(this)) == 0) {
+ ret.index = i;
+ return ret;
+ }
+ }
+ return BelId();
+}
+
+BelId Arch::getBelByLocation(Loc loc) const
+{
+ BelId ret;
+
+ if (loc.x >= chip_info->width || loc.y >= chip_info->height)
+ return BelId();
+
+ ret.location.x = loc.x;
+ ret.location.y = loc.y;
+
+ const TileTypePOD *tilei = tile_info(ret);
+ for (int i = 0; i < tilei->num_bels; i++) {
+ if (tilei->bel_data[i].z == loc.z) {
+ ret.index = i;
+ return ret;
+ }
+ }
+
+ return BelId();
+}
+
+BelRange Arch::getBelsByTile(int x, int y) const
+{
+ BelRange br;
+
+ br.b.cursor_tile = y * chip_info->width + x;
+ br.e.cursor_tile = y * chip_info->width + x;
+ br.b.cursor_index = 0;
+ br.e.cursor_index = chip_info->tiles[y * chip_info->width + x].num_bels - 1;
+ br.b.chip = chip_info;
+ br.e.chip = chip_info;
+ if (br.e.cursor_index == -1)
+ ++br.e.cursor_index;
+ else
+ ++br.e;
+ return br;
+}
+
+bool Arch::getBelGlobalBuf(BelId bel) const { return false; }
+
+WireId Arch::getBelPinWire(BelId bel, IdString pin) const
+{
+ NPNR_ASSERT(bel != BelId());
+
+ int num_bel_wires = tile_info(bel)->bel_data[bel.index].num_bel_wires;
+ const BelWirePOD *bel_wires = &*tile_info(bel)->bel_data[bel.index].bel_wires;
+
+ for (int i = 0; i < num_bel_wires; i++)
+ if (bel_wires[i].port == pin.index) {
+ WireId ret;
+
+ ret.location.x = bel_wires[i].rel_wire_loc.x;
+ ret.location.y = bel_wires[i].rel_wire_loc.y;
+ ret.index = bel_wires[i].wire_index;
+
+ return ret;
+ }
+
+ return WireId();
+}
+
+PortType Arch::getBelPinType(BelId bel, IdString pin) const
+{
+ NPNR_ASSERT(bel != BelId());
+
+ int num_bel_wires = tile_info(bel)->bel_data[bel.index].num_bel_wires;
+ const BelWirePOD *bel_wires = &*tile_info(bel)->bel_data[bel.index].bel_wires;
+
+ for (int i = 0; i < num_bel_wires; i++)
+ if (bel_wires[i].port == pin.index)
+ return PortType(bel_wires[i].dir);
+
+ return PORT_INOUT;
+}
+
+std::vector<IdString> Arch::getBelPins(BelId bel) const
+{
+ std::vector<IdString> ret;
+ NPNR_ASSERT(bel != BelId());
+
+ int num_bel_wires = tile_info(bel)->bel_data[bel.index].num_bel_wires;
+ const BelWirePOD *bel_wires = &*tile_info(bel)->bel_data[bel.index].bel_wires;
+
+ for (int i = 0; i < num_bel_wires; i++) {
+ IdString id(bel_wires[i].port);
+ ret.push_back(id);
+ }
+
+ return ret;
+}
+
+// ---------------------------------------------------------------
+
+BelId Arch::getPackagePinBel(const std::string &pin) const
+{
+ for (int i = 0; i < package_info->num_pins; i++) {
+ if (package_info->pin_data[i].name.get() == pin) {
+ BelId bel;
+ bel.location = package_info->pin_data[i].abs_loc;
+ bel.index = package_info->pin_data[i].bel_index;
+ return bel;
+ }
+ }
+ return BelId();
+}
+
+// ---------------------------------------------------------------
+
+WireId Arch::getWireByName(IdStringList name) const
+{
+ if (name.size() != 3)
+ return WireId();
+ WireId ret;
+ Location loc;
+ loc.x = id_to_x.at(name[0]);
+ loc.y = id_to_y.at(name[1]);
+ ret.location = loc;
+ const TileTypePOD *loci = tile_info(ret);
+ for (int i = 0; i < loci->num_wires; i++) {
+ if (std::strcmp(loci->wire_data[i].name.get(), name[2].c_str(this)) == 0) {
+ ret.index = i;
+ return ret;
+ }
+ }
+ return WireId();
+}
+
+// ---------------------------------------------------------------
+
+PipId Arch::getPipByName(IdStringList name) const
+{
+ if (name.size() != 3)
+ return PipId();
+ auto it = pip_by_name.find(name);
+ if (it != pip_by_name.end())
+ return it->second;
+
+ PipId ret;
+ Location loc;
+ std::string basename;
+ loc.x = id_to_x.at(name[0]);
+ loc.y = id_to_y.at(name[1]);
+ ret.location = loc;
+ const TileTypePOD *loci = tile_info(ret);
+ for (int i = 0; i < loci->num_pips; i++) {
+ PipId curr;
+ curr.location = loc;
+ curr.index = i;
+ pip_by_name[getPipName(curr)] = curr;
+ }
+ if (pip_by_name.find(name) == pip_by_name.end())
+ NPNR_ASSERT_FALSE_STR("no pip named " + name.str(getCtx()));
+ return pip_by_name[name];
+}
+
+IdStringList Arch::getPipName(PipId pip) const
+{
+ auto &pip_data = tile_info(pip)->pips_data[pip.index];
+ WireId src = getPipSrcWire(pip), dst = getPipDstWire(pip);
+ const char *src_name = tile_info(src)->wire_data[src.index].name.get();
+ const char *dst_name = tile_info(dst)->wire_data[dst.index].name.get();
+ std::string pip_name =
+ stringf("%d_%d_%s->%d_%d_%s", pip_data.src.x - pip.location.x, pip_data.src.y - pip.location.y, src_name,
+ pip_data.dst.x - pip.location.x, pip_data.dst.y - pip.location.y, dst_name);
+
+ std::array<IdString, 3> ids{x_ids.at(pip.location.x), y_ids.at(pip.location.y), id(pip_name)};
+ return IdStringList(ids);
+}
+
+// ---------------------------------------------------------------
+
+delay_t Arch::estimateDelay(WireId src, WireId dst) const
+{
+ // Taxicab distance multiplied by pipDelay (0.01) and fake wireDelay (0.01).
+ // TODO: This function will not work well for entrance to global routing,
+ // as the entrances are located physically far from the DCCAs.
+ return (abs(dst.location.x - src.location.x) + abs(dst.location.y - src.location.y)) * (0.01 + 0.01);
+}
+
+delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
+{
+ BelId src = net_info->driver.cell->bel;
+ BelId dst = sink.cell->bel;
+
+ NPNR_ASSERT(src != BelId());
+ NPNR_ASSERT(dst != BelId());
+
+ // TODO: Same deal applies here as with estimateDelay.
+ return (abs(dst.location.x - src.location.x) + abs(dst.location.y - src.location.y)) * (0.01 + 0.01);
+}
+
+ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const
+{
+ ArcBounds bb;
+ bb.x0 = std::min(src.location.x, dst.location.x);
+ bb.y0 = std::min(src.location.y, dst.location.y);
+ bb.x1 = std::max(src.location.x, dst.location.x);
+ bb.y1 = std::max(src.location.y, dst.location.y);
+ return bb;
+}
+
+// ---------------------------------------------------------------
+
+bool Arch::place()
+{
+ std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
+ if (placer == "sa") {
+ bool retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
+ getCtx()->settings[getCtx()->id("place")] = 1;
+ archInfoToAttributes();
+ return retVal;
+ } else if (placer == "heap") {
+ PlacerHeapCfg cfg(getCtx());
+ cfg.ioBufTypes.insert(id_FACADE_IO);
+ bool retVal = placer_heap(getCtx(), cfg);
+ getCtx()->settings[getCtx()->id("place")] = 1;
+ archInfoToAttributes();
+ return retVal;
+ } else {
+ log_error("MachXO2 architecture does not support placer '%s'\n", placer.c_str());
+ }
+}
+
+bool Arch::route()
+{
+ std::string router = str_or_default(settings, id("router"), defaultRouter);
+ bool result;
+ if (router == "router1") {
+ result = router1(getCtx(), Router1Cfg(getCtx()));
+ } else if (router == "router2") {
+ router2(getCtx(), Router2Cfg(getCtx()));
+ result = true;
+ } else {
+ log_error("MachXO2 architecture does not support router '%s'\n", router.c_str());
+ }
+ getCtx()->settings[getCtx()->id("route")] = 1;
+ archInfoToAttributes();
+ return result;
+}
+
+// ---------------------------------------------------------------
+bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
+{
+ // FIXME: Unlike ECP5, SLICEs in a given tile do not share a clock, so
+ // any SLICE Cell is valid for any BEL, even if some cells are already
+ // bound to BELs in the tile. However, this may need to be filled in once
+ // more than one LUT4 and DFF type is supported.
+ return true;
+}
+
+bool Arch::isBelLocationValid(BelId bel) const
+{
+ // FIXME: Same deal as isValidBelForCell.
+ return true;
+}
+
+#ifdef WITH_HEAP
+const std::string Arch::defaultPlacer = "heap";
+#else
+const std::string Arch::defaultPlacer = "sa";
+#endif
+
+const std::vector<std::string> Arch::availablePlacers = {"sa",
+#ifdef WITH_HEAP
+ "heap"
+#endif
+};
+
+const std::string Arch::defaultRouter = "router1";
+const std::vector<std::string> Arch::availableRouters = {"router1", "router2"};
+
+bool Arch::cells_compatible(const CellInfo **cells, int count) const { return false; }
+
+std::vector<std::pair<std::string, std::string>> Arch::get_tiles_at_location(int row, int col)
+{
+ std::vector<std::pair<std::string, std::string>> ret;
+ auto &tileloc = chip_info->tile_info[row * chip_info->width + col];
+ for (int i = 0; i < tileloc.num_tiles; i++) {
+ ret.push_back(std::make_pair(tileloc.tile_names[i].name.get(),
+ chip_info->tiletype_names[tileloc.tile_names[i].type_idx].get()));
+ }
+ return ret;
+}
+
+NEXTPNR_NAMESPACE_END