From 561b519716e86576f500dc91b676aad6b6166afc Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Tue, 26 Jan 2021 10:05:23 -0800 Subject: Initial FPGA interchange (which is just a cut-down xilinx arch). Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- CMakeLists.txt | 4 +- fpga_interchange/arch.cc | 474 +++++++++++ fpga_interchange/arch.h | 1096 ++++++++++++++++++++++++++ fpga_interchange/arch_pybindings.cc | 74 ++ fpga_interchange/arch_pybindings.h | 97 +++ fpga_interchange/archdefs.h | 189 +++++ fpga_interchange/constids.inc | 0 fpga_interchange/family.cmake | 0 fpga_interchange/fpga_interchange_archdefs.h | 87 ++ fpga_interchange/main.cc | 85 ++ 10 files changed, 2104 insertions(+), 2 deletions(-) create mode 100644 fpga_interchange/arch.cc create mode 100644 fpga_interchange/arch.h create mode 100644 fpga_interchange/arch_pybindings.cc create mode 100644 fpga_interchange/arch_pybindings.h create mode 100644 fpga_interchange/archdefs.h create mode 100644 fpga_interchange/constids.inc create mode 100644 fpga_interchange/family.cmake create mode 100644 fpga_interchange/fpga_interchange_archdefs.h create mode 100644 fpga_interchange/main.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index b6fddcf1..1bfef987 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,9 +66,9 @@ endif() set(PROGRAM_PREFIX "" CACHE STRING "Name prefix for executables") # List of families to build -set(FAMILIES generic ice40 ecp5 nexus gowin) +set(FAMILIES generic ice40 ecp5 nexus gowin fpga_interchange) set(STABLE_FAMILIES generic ice40 ecp5) -set(EXPERIMENTAL_FAMILIES nexus gowin) +set(EXPERIMENTAL_FAMILIES nexus gowin fpga_interchange) set(ARCH "" CACHE STRING "Architecture family for nextpnr build") set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES}) diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc new file mode 100644 index 00000000..d905e7ed --- /dev/null +++ b/fpga_interchange/arch.cc @@ -0,0 +1,474 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018-19 David Shah + * + * 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 +#include +#include +#include +#include +#include +#include "log.h" +#include "nextpnr.h" +#include "placer1.h" +#include "placer_heap.h" +#include "router1.h" +#include "router2.h" +#include "timing.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +static std::pair split_identifier_name(const std::string &name) +{ + size_t first_slash = name.find('/'); + NPNR_ASSERT(first_slash != std::string::npos); + return std::make_pair(name.substr(0, first_slash), name.substr(first_slash + 1)); +}; + +static std::pair split_identifier_name_dot(const std::string &name) +{ + size_t first_dot = name.find('.'); + NPNR_ASSERT(first_dot != std::string::npos); + return std::make_pair(name.substr(0, first_dot), name.substr(first_dot + 1)); +}; + +// ----------------------------------------------------------------------- + +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(const RelPtr *ptr) { return ptr->get(); } + +Arch::Arch(ArchArgs args) : args(args) +{ + try { + blob_file.open(args.chipdb); + if (args.chipdb.empty() || !blob_file.is_open()) + log_error("Unable to read chipdb %s\n", args.chipdb.c_str()); + const char *blob = reinterpret_cast(blob_file.data()); + chip_info = get_chip_info(reinterpret_cast *>(blob)); + } catch (...) { + log_error("Unable to read chipdb %s\n", args.chipdb.c_str()); + } + + tileStatus.resize(chip_info->num_tiles); + for (int i = 0; i < chip_info->num_tiles; i++) { + tileStatus[i].boundcells.resize(chip_info->tile_types[chip_info->tiles[i].type].num_bels); + } +} + +// ----------------------------------------------------------------------- + +std::string Arch::getChipName() const { return chip_info->name.get(); } + +// ----------------------------------------------------------------------- + +IdString Arch::archArgsToId(ArchArgs args) const { return IdString(); } + +// ----------------------------------------------------------------------- + +void Arch::setup_byname() const +{ + if (tile_by_name.empty()) { + for (int i = 0; i < chip_info->num_tiles; i++) { + tile_by_name[chip_info->tiles[i].name.get()] = i; + } + } + + if (site_by_name.empty()) { + for (int i = 0; i < chip_info->num_tiles; i++) { + auto &tile = chip_info->tiles[i]; + auto &tile_type = chip_info->tile_types[tile.type]; + for (int j = 0; j < tile_type.number_sites; j++) { + auto &site = chip_info->sites[tile.sites[j]]; + site_by_name[site.name.get()] = std::make_pair(i, j); + } + } + } +} + +BelId Arch::getBelByName(IdString name) const +{ + BelId ret; + + setup_byname(); + + auto split = split_identifier_name(name.str(this)); + + int tile, site; + std::tie(tile, site) = site_by_name.at(split.first); + auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type]; + IdString belname = id(split.second); + for (int i = 0; i < tile_info.num_bels; i++) { + if (tile_info.bel_data[i].site == site && tile_info.bel_data[i].name == belname.index) { + ret.tile = tile; + ret.index = i; + break; + } + } + + return ret; +} + +BelRange Arch::getBelsByTile(int x, int y) const +{ + BelRange br; + + br.b.cursor_tile = getTileIndex(x, y); + br.e.cursor_tile = br.b.cursor_tile; + br.b.cursor_index = 0; + br.e.cursor_index = chip_info->tile_types[chip_info->tiles[br.b.cursor_tile].type].num_bels; + 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; +} + +WireId Arch::getBelPinWire(BelId bel, IdString pin) const +{ + NPNR_ASSERT(bel != BelId()); + + int num_bel_wires = locInfo(bel).bel_data[bel.index].num_bel_wires; + const int32_t *ports = locInfo(bel).bel_data[bel.index].ports.get(); + for (int i = 0; i < num_bel_wires; i++) { + if (ports[i] == pin.index) { + const int32_t *wires = locInfo(bel).bel_data[bel.index].wires.get(); + int32_t wire_index = wires[i]; + return canonicalWireId(chip_info, bel.tile, wire_index); + } + } + + // Port could not be found! + return WireId(); +} + +PortType Arch::getBelPinType(BelId bel, IdString pin) const +{ + NPNR_ASSERT(bel != BelId()); + + int num_bel_wires = locInfo(bel).bel_data[bel.index].num_bel_wires; + const int32_t *ports = locInfo(bel).bel_data[bel.index].ports.get(); + + for (int i = 0; i < num_bel_wires; i++) { + if (ports[i] == pin.index) { + const int32_t *types = locInfo(bel).bel_data[bel.index].types.get(); + return PortType(types[i]); + } + } + + + return PORT_INOUT; +} + +// ----------------------------------------------------------------------- + +WireId Arch::getWireByName(IdString name) const +{ + if (wire_by_name_cache.count(name)) + return wire_by_name_cache.at(name); + WireId ret; + setup_byname(); + + const std::string &s = name.str(this); + auto sp = split_identifier_name(s.substr(8)); + auto iter = site_by_name.find(sp.first); + if (iter != site_by_name.end()) { + int tile; + int site; + std::tie(tile, site) = iter->second; + auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type]; + IdString wirename = id(sp.second); + for (int i = 0; i < tile_info.num_wires; i++) { + if (tile_info.wire_data[i].site == site && tile_info.wire_data[i].name == wirename.index) { + ret.tile = tile; + ret.index = i; + break; + } + } + } else { + auto sp = split_identifier_name(s); + int tile = tile_by_name.at(sp.first); + auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type]; + IdString wirename = id(sp.second); + for (int i = 0; i < tile_info.num_wires; i++) { + if (tile_info.wire_data[i].site == -1 && tile_info.wire_data[i].name == wirename.index) { + ret.tile = tile; + ret.index = i; + break; + } + } + } + + wire_by_name_cache[name] = ret; + + return ret; +} + +IdString Arch::getWireType(WireId wire) const { return id(""); } +std::vector> Arch::getWireAttrs(WireId wire) const +{ + return {}; +} + +// ----------------------------------------------------------------------- + +PipId Arch::getPipByName(IdString name) const +{ + if (pip_by_name_cache.count(name)) + return pip_by_name_cache.at(name); + + PipId ret; + setup_byname(); + + const std::string &s = name.str(this); + auto sp = split_identifier_name(s.substr(8)); + auto iter = site_by_name.find(sp.first); + if (iter != site_by_name.end()) { + int tile; + int site; + std::tie(tile, site) = iter->second; + auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type]; + auto sp3 = split_identifier_name(sp.second); + IdString belname = id(sp3.first); + IdString pinname = id(sp3.second); + for (int i = 0; i < tile_info.num_pips; i++) { + if (tile_info.pip_data[i].site == site && tile_info.pip_data[i].bel == belname.index && + tile_info.pip_data[i].extra_data == pinname.index) { + ret.tile = tile; + ret.index = i; + break; + } + } + } else { + int tile = tile_by_name.at(sp.first); + auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type]; + + auto spn = split_identifier_name_dot(sp.second); + int fromwire = std::stoi(spn.first), towire = std::stoi(spn.second); + + for (int i = 0; i < tile_info.num_pips; i++) { + if (tile_info.pip_data[i].src_index == fromwire && + tile_info.pip_data[i].dst_index == towire) { + ret.tile = tile; + ret.index = i; + break; + } + } + } + + pip_by_name_cache[name] = ret; + + return ret; +} + +IdString Arch::getPipName(PipId pip) const +{ + NPNR_ASSERT(pip != PipId()); + if (locInfo(pip).pip_data[pip.index].site != -1) { + auto site_index = chip_info->tiles[pip.tile].sites[locInfo(pip).pip_data[pip.index].site]; + auto &site = chip_info->sites[site_index]; + return id(site.name.get() + std::string("/") + IdString(locInfo(pip).pip_data[pip.index].bel).str(this) + "/" + + IdString(locInfo(pip).wire_data[locInfo(pip).pip_data[pip.index].src_index].name).str(this)); + } else { + return id(std::string(chip_info->tiles[pip.tile].name.get()) + "/" + + std::to_string(locInfo(pip).pip_data[pip.index].src_index) + "." + + std::to_string(locInfo(pip).pip_data[pip.index].dst_index)); + } +} + +IdString Arch::getPipType(PipId pip) const { return id("PIP"); } + +std::vector> Arch::getPipAttrs(PipId pip) const { return {}; } + +// ----------------------------------------------------------------------- + +std::vector Arch::getBelPins(BelId bel) const +{ + std::vector ret; + NPNR_ASSERT(bel != BelId()); + + // FIXME: The std::vector here can be replaced by a int32_t -> IdString + // range wrapper. + int num_bel_wires = locInfo(bel).bel_data[bel.index].num_bel_wires; + const int32_t *ports = locInfo(bel).bel_data[bel.index].ports.get(); + + for (int i = 0; i < num_bel_wires; i++) { + ret.push_back(IdString(ports[i])); + } + + return ret; +} + +BelId Arch::getBelByLocation(Loc loc) const +{ + BelId bi; + if (loc.x >= chip_info->width || loc.y >= chip_info->height) + return BelId(); + bi.tile = getTileIndex(loc); + auto &li = locInfo(bi); + + if(loc.z >= li.num_bels) { + return BelId(); + } else { + bi.index = loc.z; + return bi; + } +} + +std::vector> Arch::getBelAttrs(BelId bel) const { return {}; } + +// ----------------------------------------------------------------------- + +delay_t Arch::estimateDelay(WireId src, WireId dst, bool debug) const +{ + return 0; +} + +ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const +{ + int dst_tile = dst.tile == -1 ? chip_info->nodes[dst.index].tile_wires[0].tile : dst.tile; + int src_tile = src.tile == -1 ? chip_info->nodes[src.index].tile_wires[0].tile : src.tile; + + int x0, x1, y0, y1; + x0 = src_tile % chip_info->width; + x1 = x0; + y0 = src_tile / chip_info->width; + y1 = y0; + auto expand = [&](int x, int y) { + x0 = std::min(x0, x); + x1 = std::max(x1, x); + y0 = std::min(y0, y); + y1 = std::max(y1, y); + }; + + expand(dst_tile % chip_info->width, dst_tile / chip_info->width); + + if (source_locs.count(src)) + expand(source_locs.at(src).x, source_locs.at(src).y); + + if (sink_locs.count(dst)) { + expand(sink_locs.at(dst).x, sink_locs.at(dst).y); + } + + return {x0, y0, x1, y1}; +} + +delay_t Arch::getBoundingBoxCost(WireId src, WireId dst, int distance) const +{ + return 0; +} + +delay_t Arch::getWireRipupDelayPenalty(WireId wire) const +{ + return getRipupDelayPenalty(); +} + +delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +{ + return 0; +} + +bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } + +// ----------------------------------------------------------------------- + +bool Arch::pack() +{ + return false; +} + +bool Arch::place() +{ + return false; +} + +bool Arch::route() +{ + return false; +} + +// ----------------------------------------------------------------------- + +std::vector Arch::getDecalGraphics(DecalId decal) const +{ + return {}; +} + +DecalXY Arch::getBelDecal(BelId bel) const +{ + DecalXY decalxy; + return decalxy; +} + +DecalXY Arch::getWireDecal(WireId wire) const +{ + DecalXY decalxy; + return decalxy; +} + +DecalXY Arch::getPipDecal(PipId pip) const { return {}; }; + +DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; }; + +// ----------------------------------------------------------------------- + +bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const +{ + return false; +} + +TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const +{ + return TMG_IGNORE; +} + +TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const +{ + TimingClockingInfo info; + return info; +} + +#ifdef WITH_HEAP +const std::string Arch::defaultPlacer = "heap"; +#else +const std::string Arch::defaultPlacer = "sa"; +#endif + +const std::vector Arch::availablePlacers = {"sa", +#ifdef WITH_HEAP + "heap" +#endif +}; + +const std::string Arch::defaultRouter = "router2"; +const std::vector Arch::availableRouters = {"router1", "router2"}; + +NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h new file mode 100644 index 00000000..08e2abbb --- /dev/null +++ b/fpga_interchange/arch.h @@ -0,0 +1,1096 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018-19 David Shah + * + * + * 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. + * + */ + +#ifndef NEXTPNR_H +#error Include "arch.h" via "nextpnr.h" only. +#endif + +#include + +#include + +NEXTPNR_NAMESPACE_BEGIN + +/**** Everything in this section must be kept in sync with chipdb.py ****/ + +template struct RelPtr +{ + int32_t offset; + + // void set(const T *ptr) { + // offset = reinterpret_cast(ptr) - + // reinterpret_cast(this); + // } + + const T *get() const + { + return reinterpret_cast(reinterpret_cast(this) + int64_t(offset) * 4); + } + + const T &operator[](size_t index) const { return get()[index]; } + + const T &operator*() const { return *(get()); } + + const T *operator->() const { return get(); } +}; + + + +NPNR_PACKED_STRUCT(struct SiteTypeInfoPOD { + // Name of this site type. + RelPtr name; + + // Lookup for site pip name to site pip index. + int32_t number_site_pips; + RelPtr> site_pip_names; +}); + +// Flattened site indexing. +// +// To enable flat BelId.z spaces, every tile and sites within that tile are +// flattened. +// +// This has implications on BelId's, WireId's and PipId's. +// The flattened site space works as follows: +// - Objects that belong to the tile are first. BELs are always part of Sites, +// so no BEL objects are in this category. +// - All site alternative modes are exposed as a "full" site. +// - Each site appends it's BEL's, wires (site wires) and PIP's. +// - Sites add two types of pips. Sites will add pip data first for site +// pips, and then for site pin edges. +// 1. The first type is site pips, which connect site wires to other site +// wires. +// 2. The second type is site pin edges, which connect site wires to tile +// wires (or vise-versa). + +NPNR_PACKED_STRUCT(struct BelInfoPOD { + int32_t name; // bel name (in site) constid + int32_t type; // Type name constid + + int32_t num_bel_wires; + RelPtr ports; // port name constid + RelPtr types; // port name (IN/OUT/BIDIR) + RelPtr wires; // connected wire index in tile, or -1 if NA + + int16_t site; + int16_t site_variant; // some sites have alternative types + int16_t is_routing; + int16_t padding; +}); + +NPNR_PACKED_STRUCT(struct BelPortPOD { + int32_t bel_index; + int32_t port; +}); + +NPNR_PACKED_STRUCT(struct TileWireInfoPOD { + int32_t name; + int32_t num_uphill, num_downhill; + + // Pip index inside tile + RelPtr pips_uphill, pips_downhill; + + // Bel index inside tile + int32_t num_bel_pins; + RelPtr bel_pins; + + int16_t site; // site index in tile + int16_t site_variant; // site variant index in tile +}); + +NPNR_PACKED_STRUCT(struct PipInfoPOD { + int32_t src_index, dst_index; + int16_t site; // site index in tile + int16_t site_variant; // site variant index in tile + int16_t bel; // BEL this pip belongs to if site pip. + int16_t extra_data; +}); + +NPNR_PACKED_STRUCT(struct TileTypeInfoPOD { + int32_t name; // Tile type constid + + int32_t number_sites; + + int32_t num_bels; + RelPtr bel_data; + + int32_t num_wires; + RelPtr wire_data; + + int32_t num_pips; + RelPtr pip_data; +}); + +NPNR_PACKED_STRUCT(struct SiteInstInfoPOD { + RelPtr name; + + // Which site type is this site instance? + // constid + int32_t site_type; +}); + +NPNR_PACKED_STRUCT(struct TileInstInfoPOD { + // Name of this tile. + RelPtr name; + + // Index into root.tile_types. + int32_t type; + + // This array is root.tile_types[type].number_sites long. + RelPtr sites; + + // Number of tile wires; excluding any site-internal wires + // which come after general wires and are not stored here + // as they will never be nodal + int32_t num_tile_wires; + // -1 if a tile-local wire; node index if nodal wire + RelPtr tile_wire_to_node; +}); + +NPNR_PACKED_STRUCT(struct TileWireRefPOD { + int32_t tile; + int32_t index; +}); + +NPNR_PACKED_STRUCT(struct NodeInfoPOD { + int32_t num_tile_wires; + RelPtr tile_wires; +}); + +NPNR_PACKED_STRUCT(struct ChipInfoPOD { + RelPtr name; + RelPtr generator; + + int32_t version; + int32_t width, height; + int32_t num_tiles, num_tile_types; + int32_t num_sites, num_nodes; + RelPtr tile_types; + RelPtr sites; + RelPtr tiles; + RelPtr nodes; +}); + +/************************ End of chipdb section. ************************/ + +inline const TileTypeInfoPOD &tileInfo(const ChipInfoPOD *chip_info, int32_t tile) +{ + return chip_info->tile_types[chip_info->tiles[tile].type]; +} + +struct BelIterator +{ + const ChipInfoPOD *chip; + int cursor_index; + int cursor_tile; + + BelIterator operator++() + { + cursor_index++; + while (cursor_tile < chip->num_tiles && + cursor_index >= tileInfo(chip, cursor_tile).num_bels) { + cursor_index = 0; + cursor_tile++; + } + return *this; + } + BelIterator operator++(int) + { + BelIterator prior(*this); + ++(*this); + return prior; + } + + bool operator!=(const BelIterator &other) const + { + return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile; + } + + bool operator==(const BelIterator &other) const + { + return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile; + } + + BelId operator*() const + { + BelId ret; + ret.tile = cursor_tile; + ret.index = cursor_index; + return ret; + } +}; + +struct BelRange +{ + BelIterator b, e; + BelIterator begin() const { return b; } + BelIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + +// Iterate over TileWires for a wire (will be more than one if nodal) +struct TileWireIterator +{ + const ChipInfoPOD *chip; + WireId baseWire; + int cursor = -1; + + void operator++() { cursor++; } + bool operator!=(const TileWireIterator &other) const { return cursor != other.cursor; } + + // Returns a *denormalised* identifier always pointing to a tile wire rather than a node + WireId operator*() const + { + if (baseWire.tile == -1) { + WireId tw; + const auto &node_wire = chip->nodes[baseWire.index].tile_wires[cursor]; + tw.tile = node_wire.tile; + tw.index = node_wire.index; + return tw; + } else { + return baseWire; + } + } +}; + +struct TileWireRange +{ + TileWireIterator b, e; + TileWireIterator begin() const { return b; } + TileWireIterator end() const { return e; } +}; + +inline WireId canonicalWireId(const ChipInfoPOD *chip_info, int32_t tile, int32_t wire) +{ + WireId id; + + if (wire >= chip_info->tiles[tile].num_tile_wires) { + // Cannot be a nodal wire + id.tile = tile; + id.index = wire; + } else { + int32_t node = chip_info->tiles[tile].tile_wire_to_node[wire]; + if (node == -1) { + // Not a nodal wire + id.tile = tile; + id.index = wire; + } else { + // Is a nodal wire, set tile to -1 + id.tile = -1; + id.index = node; + } + } + + return id; +} + +// ----------------------------------------------------------------------- + +struct WireIterator +{ + const ChipInfoPOD *chip; + int cursor_index = 0; + int cursor_tile = -1; + + WireIterator operator++() + { + // Iterate over nodes first, then tile wires that aren't nodes + do { + cursor_index++; + if (cursor_tile == -1 && cursor_index >= chip->num_nodes) { + cursor_tile = 0; + cursor_index = 0; + } + while (cursor_tile != -1 && cursor_tile < chip->num_tiles && + cursor_index >= chip->tile_types[chip->tiles[cursor_tile].type].num_wires) { + cursor_index = 0; + cursor_tile++; + } + + } while ((cursor_tile != -1 && cursor_tile < chip->num_tiles && + cursor_index < chip->tiles[cursor_tile].num_tile_wires && + chip->tiles[cursor_tile].tile_wire_to_node[cursor_index] != -1)); + + return *this; + } + WireIterator operator++(int) + { + WireIterator prior(*this); + ++(*this); + return prior; + } + + bool operator!=(const WireIterator &other) const + { + return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile; + } + + bool operator==(const WireIterator &other) const + { + return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile; + } + + WireId operator*() const + { + WireId ret; + ret.tile = cursor_tile; + ret.index = cursor_index; + return ret; + } +}; + +struct WireRange +{ + WireIterator b, e; + WireIterator begin() const { return b; } + WireIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- +struct AllPipIterator +{ + const ChipInfoPOD *chip; + int cursor_index; + int cursor_tile; + + AllPipIterator operator++() + { + cursor_index++; + while (cursor_tile < chip->num_tiles && + cursor_index >= chip->tile_types[chip->tiles[cursor_tile].type].num_pips) { + cursor_index = 0; + cursor_tile++; + } + return *this; + } + AllPipIterator operator++(int) + { + AllPipIterator prior(*this); + ++(*this); + return prior; + } + + bool operator!=(const AllPipIterator &other) const + { + return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile; + } + + bool operator==(const AllPipIterator &other) const + { + return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile; + } + + PipId operator*() const + { + PipId ret; + ret.tile = cursor_tile; + ret.index = cursor_index; + return ret; + } +}; + +struct AllPipRange +{ + AllPipIterator b, e; + AllPipIterator begin() const { return b; } + AllPipIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + +struct UphillPipIterator +{ + const ChipInfoPOD *chip; + TileWireIterator twi, twi_end; + int cursor = -1; + + void operator++() + { + cursor++; + while (true) { + if (!(twi != twi_end)) + break; + WireId w = *twi; + auto &tile = chip->tile_types[chip->tiles[w.tile].type]; + if (cursor < tile.wire_data[w.index].num_uphill) + break; + ++twi; + cursor = 0; + } + } + bool operator!=(const UphillPipIterator &other) const { return twi != other.twi || cursor != other.cursor; } + + PipId operator*() const + { + PipId ret; + WireId w = *twi; + ret.tile = w.tile; + ret.index = chip->tile_types[chip->tiles[w.tile].type].wire_data[w.index].pips_uphill[cursor]; + return ret; + } +}; + +struct UphillPipRange +{ + UphillPipIterator b, e; + UphillPipIterator begin() const { return b; } + UphillPipIterator end() const { return e; } +}; + +struct DownhillPipIterator +{ + const ChipInfoPOD *chip; + TileWireIterator twi, twi_end; + int cursor = -1; + + void operator++() + { + cursor++; + while (true) { + if (!(twi != twi_end)) + break; + WireId w = *twi; + auto &tile = chip->tile_types[chip->tiles[w.tile].type]; + if (cursor < tile.wire_data[w.index].num_downhill) + break; + ++twi; + cursor = 0; + } + } + bool operator!=(const DownhillPipIterator &other) const { return twi != other.twi || cursor != other.cursor; } + + PipId operator*() const + { + PipId ret; + WireId w = *twi; + ret.tile = w.tile; + ret.index = chip->tile_types[chip->tiles[w.tile].type].wire_data[w.index].pips_downhill[cursor]; + return ret; + } +}; + +struct DownhillPipRange +{ + DownhillPipIterator b, e; + DownhillPipIterator begin() const { return b; } + DownhillPipIterator end() const { return e; } +}; + +struct BelPinIterator +{ + const ChipInfoPOD *chip; + TileWireIterator twi, twi_end; + int cursor = -1; + + void operator++() + { + cursor++; + while (true) { + if (!(twi != twi_end)) + break; + WireId w = *twi; + auto &tile = tileInfo(chip, w.tile); + if (cursor < tile.wire_data[w.index].num_bel_pins) + break; + ++twi; + cursor = 0; + } + } + bool operator!=(const BelPinIterator &other) const { return twi != other.twi || cursor != other.cursor; } + + BelPin operator*() const + { + BelPin ret; + WireId w = *twi; + ret.bel.tile = w.tile; + ret.bel.index = tileInfo(chip, w.tile).wire_data[w.index].bel_pins[cursor].bel_index; + ret.pin.index = tileInfo(chip, w.tile).wire_data[w.index].bel_pins[cursor].port; + return ret; + } +}; + +struct BelPinRange +{ + BelPinIterator b, e; + BelPinIterator begin() const { return b; } + BelPinIterator end() const { return e; } +}; + +struct ArchArgs +{ + std::string chipdb; +}; + +struct Arch : BaseCtx +{ + boost::iostreams::mapped_file_source blob_file; + const ChipInfoPOD *chip_info; + + mutable std::unordered_map tile_by_name; + mutable std::unordered_map> site_by_name; + + std::unordered_map wire_to_net; + std::unordered_map pip_to_net; + std::unordered_map> driving_pip_loc; + std::unordered_map reserved_wires; + + struct TileStatus + { + std::vector boundcells; + }; + + std::vector tileStatus; + + ArchArgs args; + Arch(ArchArgs args); + + std::string getChipName() const; + + IdString archId() const { return id(chip_info->name.get()); } + ArchArgs archArgs() const { return args; } + IdString archArgsToId(ArchArgs args) const; + + // ------------------------------------------------- + + uint32_t getTileIndex(int x, int y) const { + return (y * chip_info->width + x); + } + uint32_t getTileIndex(Loc loc) const { + return getTileIndex(loc.x, loc.y); + } + template void getTileXY(TileIndex tile_index, CoordIndex *x, CoordIndex *y) const { + *x = tile_index % chip_info->width; + *y = tile_index / chip_info->width; + } + + template void getTileLoc(TileIndex tile_index, Loc * loc) const { + getTileXY(tile_index, &loc->x, &loc->y); + } + + int getGridDimX() const { return chip_info->width; } + int getGridDimY() const { return chip_info->height; } + int getTileBelDimZ(int x, int y) const { + return chip_info->tile_types[chip_info->tiles[getTileIndex(x, y)].type].num_bels; + } + int getTilePipDimZ(int x, int y) const { + return chip_info->tile_types[chip_info->tiles[getTileIndex(x, y)].type].number_sites; + } + + // ------------------------------------------------- + + void setup_byname() const; + + BelId getBelByName(IdString name) const; + + IdString getBelName(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + int site_index = locInfo(bel).bel_data[bel.index].site; + NPNR_ASSERT(site_index != -1); + const SiteInstInfoPOD &site = chip_info->sites[chip_info->tiles[bel.tile].sites[site_index]]; + return id(std::string(site.name.get()) + + "/" + IdString(locInfo(bel).bel_data[bel.index].name).str(this)); + } + + uint32_t getBelChecksum(BelId bel) const { return bel.index; } + + void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) + { + NPNR_ASSERT(bel != BelId()); + NPNR_ASSERT(tileStatus[bel.tile].boundcells[bel.index] == nullptr); + + tileStatus[bel.tile].boundcells[bel.index] = cell; + cell->bel = bel; + cell->belStrength = strength; + refreshUiBel(bel); + } + + void unbindBel(BelId bel) + { + NPNR_ASSERT(bel != BelId()); + NPNR_ASSERT(tileStatus[bel.tile].boundcells[bel.index] != nullptr); + tileStatus[bel.tile].boundcells[bel.index]->bel = BelId(); + tileStatus[bel.tile].boundcells[bel.index]->belStrength = STRENGTH_NONE; + tileStatus[bel.tile].boundcells[bel.index] = nullptr; + refreshUiBel(bel); + } + + bool checkBelAvail(BelId bel) const + { + return tileStatus[bel.tile].boundcells[bel.index] == nullptr; + } + + CellInfo *getBoundBelCell(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return tileStatus[bel.tile].boundcells[bel.index]; + } + + CellInfo *getConflictingBelCell(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return tileStatus[bel.tile].boundcells[bel.index]; + } + + BelRange getBels() const + { + BelRange range; + range.b.cursor_tile = 0; + range.b.cursor_index = -1; + range.b.chip = chip_info; + ++range.b; //-1 and then ++ deals with the case of no Bels in the first tile + range.e.cursor_tile = chip_info->width * chip_info->height; + range.e.cursor_index = 0; + range.e.chip = chip_info; + return range; + } + + Loc getBelLocation(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + Loc loc; + getTileXY(bel.tile, &loc.x, &loc.y); + loc.z = bel.index; + return loc; + } + + BelId getBelByLocation(Loc loc) const; + BelRange getBelsByTile(int x, int y) const; + + bool getBelGlobalBuf(BelId bel) const + { + // TODO: This probably needs to be fixed! + return false; + } + + bool getBelHidden(BelId bel) const { return locInfo(bel).bel_data[bel.index].is_routing; } + + IdString getBelType(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + return IdString(locInfo(bel).bel_data[bel.index].type); + } + + std::vector> getBelAttrs(BelId bel) const; + + WireId getBelPinWire(BelId bel, IdString pin) const; + PortType getBelPinType(BelId bel, IdString pin) const; + std::vector getBelPins(BelId bel) const; + + bool isBelLocked(BelId bel) const; + + // ------------------------------------------------- + + mutable std::unordered_map wire_by_name_cache; + + WireId getWireByName(IdString name) const; + + const TileWireInfoPOD &wireInfo(WireId wire) const + { + if (wire.tile == -1) { + const TileWireRefPOD &wr = chip_info->nodes[wire.index].tile_wires[0]; + return chip_info->tile_types[chip_info->tiles[wr.tile].type].wire_data[wr.index]; + } else { + return locInfo(wire).wire_data[wire.index]; + } + } + + IdString getWireName(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + if (wire.tile != -1 && locInfo(wire).wire_data[wire.index].site != -1) { + int site_index = locInfo(wire).wire_data[wire.index].site; + const SiteInstInfoPOD &site = chip_info->sites[chip_info->tiles[wire.tile].sites[site_index]]; + return id(site.name.get() + + std::string("/") + IdString(locInfo(wire).wire_data[wire.index].name).str(this)); + } else { + return id(std::string(chip_info + ->tiles[wire.tile == -1 ? chip_info->nodes[wire.index].tile_wires[0].tile + : wire.tile] + .name.get()) + + "/" + IdString(wireInfo(wire).name).c_str(this)); + } + } + + IdString getWireType(WireId wire) const; + std::vector> getWireAttrs(WireId wire) const; + + uint32_t getWireChecksum(WireId wire) const { return wire.index; } + + void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) + { + NPNR_ASSERT(wire != WireId()); + NPNR_ASSERT(wire_to_net[wire] == nullptr); + wire_to_net[wire] = net; + net->wires[wire].pip = PipId(); + net->wires[wire].strength = strength; + refreshUiWire(wire); + } + + void unbindWire(WireId wire) + { + NPNR_ASSERT(wire != WireId()); + NPNR_ASSERT(wire_to_net[wire] != nullptr); + + auto &net_wires = wire_to_net[wire]->wires; + auto it = net_wires.find(wire); + NPNR_ASSERT(it != net_wires.end()); + + auto pip = it->second.pip; + if (pip != PipId()) { + pip_to_net[pip] = nullptr; + } + + net_wires.erase(it); + wire_to_net[wire] = nullptr; + refreshUiWire(wire); + } + + bool checkWireAvail(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + auto w2n = wire_to_net.find(wire); + return w2n == wire_to_net.end() || w2n->second == nullptr; + } + + NetInfo *getReservedWireNet(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + auto w2n = reserved_wires.find(wire); + return w2n == reserved_wires.end() ? nullptr : w2n->second; + } + + NetInfo *getBoundWireNet(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + auto w2n = wire_to_net.find(wire); + return w2n == wire_to_net.end() ? nullptr : w2n->second; + } + + WireId getConflictingWireWire(WireId wire) const { return wire; } + + NetInfo *getConflictingWireNet(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + auto w2n = wire_to_net.find(wire); + return w2n == wire_to_net.end() ? nullptr : w2n->second; + } + + DelayInfo getWireDelay(WireId wire) const + { + DelayInfo delay; + delay.delay = 0; + return delay; + } + + TileWireRange getTileWireRange(WireId wire) const + { + TileWireRange range; + range.b.chip = chip_info; + range.b.baseWire = wire; + range.b.cursor = -1; + ++range.b; + + range.e.chip = chip_info; + range.e.baseWire = wire; + if (wire.tile == -1) + range.e.cursor = chip_info->nodes[wire.index].num_tile_wires; + else + range.e.cursor = 1; + return range; + } + + BelPinRange getWireBelPins(WireId wire) const + { + BelPinRange range; + NPNR_ASSERT(wire != WireId()); + TileWireRange twr = getTileWireRange(wire); + range.b.chip = chip_info; + range.b.twi = twr.b; + range.b.twi_end = twr.e; + range.b.cursor = -1; + ++range.b; + range.e.chip = chip_info; + range.e.twi = twr.e; + range.e.twi_end = twr.e; + range.e.cursor = 0; + return range; + } + + WireRange getWires() const + { + WireRange range; + range.b.chip = chip_info; + range.b.cursor_tile = -1; + range.b.cursor_index = 0; + range.e.chip = chip_info; + range.e.cursor_tile = chip_info->num_tiles; + range.e.cursor_index = 0; + return range; + } + + // ------------------------------------------------- + + mutable std::unordered_map pip_by_name_cache; + + PipId getPipByName(IdString name) const; + + void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) + { + NPNR_ASSERT(pip != PipId()); + NPNR_ASSERT(pip_to_net[pip] == nullptr); + + WireId dst = canonicalWireId(chip_info, pip.tile, locInfo(pip).pip_data[pip.index].dst_index); + NPNR_ASSERT(wire_to_net[dst] == nullptr || wire_to_net[dst] == net); + + pip_to_net[pip] = net; + std::pair loc; + getTileXY(pip.tile, &loc.first, &loc.second); + driving_pip_loc[dst] = loc; + + wire_to_net[dst] = net; + net->wires[dst].pip = pip; + net->wires[dst].strength = strength; + refreshUiPip(pip); + refreshUiWire(dst); + } + + void unbindPip(PipId pip) + { + NPNR_ASSERT(pip != PipId()); + NPNR_ASSERT(pip_to_net[pip] != nullptr); + + WireId dst = canonicalWireId(chip_info, pip.tile, locInfo(pip).pip_data[pip.index].dst_index); + NPNR_ASSERT(wire_to_net[dst] != nullptr); + wire_to_net[dst] = nullptr; + pip_to_net[pip]->wires.erase(dst); + + pip_to_net[pip] = nullptr; + refreshUiPip(pip); + refreshUiWire(dst); + } + + bool checkPipAvail(PipId pip) const + { + NPNR_ASSERT(pip != PipId()); + return pip_to_net.find(pip) == pip_to_net.end() || pip_to_net.at(pip) == nullptr; + } + + NetInfo *getBoundPipNet(PipId pip) const + { + NPNR_ASSERT(pip != PipId()); + auto p2n = pip_to_net.find(pip); + return p2n == pip_to_net.end() ? nullptr : p2n->second; + } + + WireId getConflictingPipWire(PipId pip) const + { + return getPipDstWire(pip); + } + + NetInfo *getConflictingPipNet(PipId pip) const + { + auto p2n = pip_to_net.find(pip); + return p2n == pip_to_net.end() ? nullptr : p2n->second; + } + + AllPipRange getPips() const + { + AllPipRange range; + range.b.cursor_tile = 0; + range.b.cursor_index = -1; + range.b.chip = chip_info; + ++range.b; //-1 and then ++ deals with the case of no wries in the first tile + range.e.cursor_tile = chip_info->width * chip_info->height; + range.e.cursor_index = 0; + range.e.chip = chip_info; + return range; + } + + Loc getPipLocation(PipId pip) const + { + Loc loc; + getTileLoc(pip.tile, &loc); + loc.z = 0; + return loc; + } + + IdString getPipName(PipId pip) const; + + IdString getPipType(PipId pip) const; + std::vector> getPipAttrs(PipId pip) const; + + uint32_t getPipChecksum(PipId pip) const { return pip.index; } + + WireId getPipSrcWire(PipId pip) const + { + return canonicalWireId(chip_info, pip.tile, locInfo(pip).pip_data[pip.index].src_index); + } + + WireId getPipDstWire(PipId pip) const + { + return canonicalWireId(chip_info, pip.tile, locInfo(pip).pip_data[pip.index].dst_index); + } + + DelayInfo getPipDelay(PipId pip) const + { + return DelayInfo(); + } + + DownhillPipRange getPipsDownhill(WireId wire) const + { + DownhillPipRange range; + NPNR_ASSERT(wire != WireId()); + TileWireRange twr = getTileWireRange(wire); + range.b.chip = chip_info; + range.b.twi = twr.b; + range.b.twi_end = twr.e; + range.b.cursor = -1; + ++range.b; + range.e.chip = chip_info; + range.e.twi = twr.e; + range.e.twi_end = twr.e; + range.e.cursor = 0; + return range; + } + + UphillPipRange getPipsUphill(WireId wire) const + { + UphillPipRange range; + NPNR_ASSERT(wire != WireId()); + TileWireRange twr = getTileWireRange(wire); + range.b.chip = chip_info; + range.b.twi = twr.b; + range.b.twi_end = twr.e; + range.b.cursor = -1; + ++range.b; + range.e.chip = chip_info; + range.e.twi = twr.e; + range.e.twi_end = twr.e; + range.e.cursor = 0; + return range; + } + + UphillPipRange getWireAliases(WireId wire) const + { + UphillPipRange range; + range.b.cursor = 0; + range.b.twi.cursor = 0; + range.e.cursor = 0; + range.e.twi.cursor = 0; + return range; + } + + // ------------------------------------------------- + + GroupId getGroupByName(IdString name) const { return GroupId(); } + IdString getGroupName(GroupId group) const { return IdString(); } + std::vector getGroups() const { return {}; } + std::vector getGroupBels(GroupId group) const { return {}; } + std::vector getGroupWires(GroupId group) const { return {}; } + std::vector getGroupPips(GroupId group) const { return {}; } + std::vector getGroupGroups(GroupId group) const { return {}; } + + // ------------------------------------------------- + mutable IdString gnd_glbl, gnd_row, vcc_glbl, vcc_row; + delay_t estimateDelay(WireId src, WireId dst, bool debug = false) const; + delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const; + ArcBounds getRouteBoundingBox(WireId src, WireId dst) const; + delay_t getBoundingBoxCost(WireId src, WireId dst, int distance) const; + delay_t getDelayEpsilon() const { return 20; } + delay_t getRipupDelayPenalty() const { return 120; } + delay_t getWireRipupDelayPenalty(WireId wire) const; + float getDelayNS(delay_t v) const { return v * 0.001; } + DelayInfo getDelayFromNS(float ns) const + { + DelayInfo del; + del.delay = delay_t(ns * 1000); + return del; + } + uint32_t getDelayChecksum(delay_t v) const { return v; } + bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const; + + // ------------------------------------------------- + + bool pack(); + bool place(); + bool route(); + // ------------------------------------------------- + + std::vector getDecalGraphics(DecalId decal) const; + + DecalXY getBelDecal(BelId bel) const; + DecalXY getWireDecal(WireId wire) const; + DecalXY getPipDecal(PipId pip) const; + DecalXY getGroupDecal(GroupId group) const; + + // ------------------------------------------------- + + // Get the delay through a cell from one port to another, returning false + // if no path exists. This only considers combinational delays, as required by the Arch API + bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const; + // Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port + TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const; + // Get the TimingClockingInfo of a port + TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const; + + // ------------------------------------------------- + + // Whether or not a given cell can be placed at a given Bel + // This is not intended for Bel type checks, but finer-grained constraints + // such as conflicting set/reset signals, etc + bool isValidBelForCell(CellInfo *cell, BelId bel) const { + // FIXME: Implement this + return true; + } + + // Return true whether all Bels at a given location are valid + bool isBelLocationValid(BelId bel) const { + // FIXME: Implement this + return true; + } + + IdString getBelTileType(BelId bel) const { return IdString(locInfo(bel).name); } + + std::unordered_map sink_locs, source_locs; + // ------------------------------------------------- + void assignArchInfo() {} + + // ------------------------------------------------- + + static const std::string defaultPlacer; + static const std::vector availablePlacers; + + static const std::string defaultRouter; + static const std::vector availableRouters; + + // ------------------------------------------------- + template const TileTypeInfoPOD &locInfo(Id &id) const + { + return chip_info->tile_types[chip_info->tiles[id.tile].type]; + } + + void writePhysicalNetlist(const std::string &filename) const { + } +}; + +NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/arch_pybindings.cc b/fpga_interchange/arch_pybindings.cc new file mode 100644 index 00000000..1ad2286b --- /dev/null +++ b/fpga_interchange/arch_pybindings.cc @@ -0,0 +1,74 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * 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. + * + */ + +#ifndef NO_PYTHON + +#include "arch_pybindings.h" +#include "nextpnr.h" +#include "pybindings.h" + +NEXTPNR_NAMESPACE_BEGIN + +void arch_wrap_python(py::module &m) +{ + using namespace PythonConversion; + py::class_(m, "ArchArgs").def_readwrite("chipdb", &ArchArgs::chipdb); + + py::class_(m, "BelId").def_readwrite("index", &BelId::index); + + py::class_(m, "WireId").def_readwrite("index", &WireId::index); + + py::class_(m, "PipId").def_readwrite("index", &PipId::index); + + auto arch_cls = py::class_(m, "Arch").def(py::init()); + auto ctx_cls = py::class_(m, "Context") + .def("checksum", &Context::checksum) + .def("pack", &Context::pack) + .def("place", &Context::place) + .def("route", &Context::route); + + fn_wrapper_2a, + addr_and_unwrap, conv_from_str>::def_wrap(ctx_cls, "isValidBelForCell"); + + typedef std::unordered_map> CellMap; + typedef std::unordered_map> NetMap; + typedef std::unordered_map AliasMap; + typedef std::unordered_map HierarchyMap; + + auto belpin_cls = py::class_>(m, "BelPin"); + readonly_wrapper>::def_wrap(belpin_cls, "bel"); + readonly_wrapper>::def_wrap(belpin_cls, "pin"); + +#include "arch_pybindings_shared.h" + + WRAP_RANGE(m, Bel, conv_to_str); + WRAP_RANGE(m, Wire, conv_to_str); + WRAP_RANGE(m, AllPip, conv_to_str); + WRAP_RANGE(m, UphillPip, conv_to_str); + WRAP_RANGE(m, DownhillPip, conv_to_str); + WRAP_RANGE(m, BelPin, wrap_context); + + WRAP_MAP_UPTR(m, CellMap, "IdCellMap"); + WRAP_MAP_UPTR(m, NetMap, "IdNetMap"); + WRAP_MAP(m, HierarchyMap, wrap_context, "HierarchyMap"); +} + +NEXTPNR_NAMESPACE_END + +#endif // NO_PYTHON diff --git a/fpga_interchange/arch_pybindings.h b/fpga_interchange/arch_pybindings.h new file mode 100644 index 00000000..0ce37906 --- /dev/null +++ b/fpga_interchange/arch_pybindings.h @@ -0,0 +1,97 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * 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. + * + */ +#ifndef ARCH_PYBINDINGS_H +#define ARCH_PYBINDINGS_H +#ifndef NO_PYTHON + +#include "nextpnr.h" +#include "pybindings.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace PythonConversion { + +template <> struct string_converter +{ + BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(ctx->id(name)); } + + std::string to_str(Context *ctx, BelId id) + { + if (id == BelId()) + throw bad_wrap(); + return ctx->getBelName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); } + + std::string to_str(Context *ctx, WireId id) + { + if (id == WireId()) + throw bad_wrap(); + return ctx->getWireName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); } + + std::string to_str(Context *ctx, WireId id) + { + if (id == WireId()) + throw bad_wrap(); + return ctx->getWireName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); } + + std::string to_str(Context *ctx, PipId id) + { + if (id == PipId()) + throw bad_wrap(); + return ctx->getPipName(id).str(ctx); + } +}; + +template <> struct string_converter +{ + BelPin from_str(Context *ctx, std::string name) + { + NPNR_ASSERT_FALSE("string_converter::from_str not implemented"); + } + + std::string to_str(Context *ctx, BelPin pin) + { + if (pin.bel == BelId()) + throw bad_wrap(); + return ctx->getBelName(pin.bel).str(ctx) + "/" + pin.pin.str(ctx); + } +}; + +} // namespace PythonConversion + +NEXTPNR_NAMESPACE_END +#endif +#endif diff --git a/fpga_interchange/archdefs.h b/fpga_interchange/archdefs.h new file mode 100644 index 00000000..744fa1d3 --- /dev/null +++ b/fpga_interchange/archdefs.h @@ -0,0 +1,189 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * + * 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. + * + */ + +#ifndef NEXTPNR_H +#error Include "archdefs.h" via "nextpnr.h" only. +#endif + +NEXTPNR_NAMESPACE_BEGIN + +#include + +typedef int delay_t; + +struct DelayInfo +{ + delay_t delay = 0; + + delay_t minRaiseDelay() const { return delay; } + delay_t maxRaiseDelay() const { return delay; } + + delay_t minFallDelay() const { return delay; } + delay_t maxFallDelay() const { return delay; } + + delay_t minDelay() const { return delay; } + delay_t maxDelay() const { return delay; } + + DelayInfo operator+(const DelayInfo &other) const + { + DelayInfo ret; + ret.delay = this->delay + other.delay; + return ret; + } +}; + +// ----------------------------------------------------------------------- + +// https://bugreports.qt.io/browse/QTBUG-80789 + +#ifndef Q_MOC_RUN + +enum ConstIds +{ + ID_NONE +#define X(t) , ID_##t +#include "constids.inc" +#undef X +}; + +#define X(t) static constexpr auto id_##t = IdString(ID_##t); +#include "constids.inc" +#undef X + +#endif + +struct BelId +{ + // Tile that contains this BEL. + int32_t tile = -1; + // Index into tile type BEL array. + // BEL indicies are the same for all tiles of the same type. + int32_t index = -1; + + bool operator==(const BelId &other) const { return tile == other.tile && index == other.index; } + bool operator!=(const BelId &other) const { return tile != other.tile || index != other.index; } + bool operator<(const BelId &other) const + { + return tile < other.tile || (tile == other.tile && index < other.index); + } +}; + +struct WireId +{ + // Tile that contains this wire. + int32_t tile = -1; + int32_t index = -1; + + bool operator==(const WireId &other) const { return tile == other.tile && index == other.index; } + bool operator!=(const WireId &other) const { return tile != other.tile || index != other.index; } + bool operator<(const WireId &other) const + { + return tile < other.tile || (tile == other.tile && index < other.index); + } +}; + +struct PipId +{ + int32_t tile = -1; + int32_t index = -1; + + bool operator==(const PipId &other) const { return tile == other.tile && index == other.index; } + bool operator!=(const PipId &other) const { return tile != other.tile || index != other.index; } + bool operator<(const PipId &other) const + { + return tile < other.tile || (tile == other.tile && index < other.index); + } +}; + +struct GroupId +{ + bool operator==(const GroupId &other) const { return true; } + bool operator!=(const GroupId &other) const { return false; } +}; + +struct DecalId +{ + bool operator==(const DecalId &other) const { return true; } + bool operator!=(const DecalId &other) const { return false; } +}; + +struct ArchNetInfo +{ +}; + +struct NetInfo; + +struct ArchCellInfo +{ +}; + +NEXTPNR_NAMESPACE_END + +namespace std { +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(bel.tile)); + boost::hash_combine(seed, hash()(bel.index)); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(wire.tile)); + boost::hash_combine(seed, hash()(wire.index)); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(pip.tile)); + boost::hash_combine(seed, hash()(pip.index)); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept + { + std::size_t seed = 0; + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept + { + std::size_t seed = 0; + return seed; + } +}; +} // namespace std diff --git a/fpga_interchange/constids.inc b/fpga_interchange/constids.inc new file mode 100644 index 00000000..e69de29b diff --git a/fpga_interchange/family.cmake b/fpga_interchange/family.cmake new file mode 100644 index 00000000..e69de29b diff --git a/fpga_interchange/fpga_interchange_archdefs.h b/fpga_interchange/fpga_interchange_archdefs.h new file mode 100644 index 00000000..5495505b --- /dev/null +++ b/fpga_interchange/fpga_interchange_archdefs.h @@ -0,0 +1,87 @@ +#include + +typedef int delay_t; + +struct DelayInfo +{ + delay_t delay = 0; + + delay_t minRaiseDelay() const { return delay; } + delay_t maxRaiseDelay() const { return delay; } + + delay_t minFallDelay() const { return delay; } + delay_t maxFallDelay() const { return delay; } + + delay_t minDelay() const { return delay; } + delay_t maxDelay() const { return delay; } + + DelayInfo operator+(const DelayInfo &other) const + { + DelayInfo ret; + ret.delay = this->delay + other.delay; + return ret; + } +}; + +struct BelId +{ + // Tile that contains this BEL. + int32_t tile = -1; + // Index into tile type BEL array. + // BEL indicies are the same for all tiles of the same type. + int32_t index = -1; + + bool operator==(const BelId &other) const { return tile == other.tile && index == other.index; } + bool operator!=(const BelId &other) const { return tile != other.tile || index != other.index; } + bool operator<(const BelId &other) const + { + return tile < other.tile || (tile == other.tile && index < other.index); + } +}; + +struct WireId +{ + // Tile that contains this wire. + int32_t tile = -1; + int32_t index = -1; + + bool operator==(const WireId &other) const { return tile == other.tile && index == other.index; } + bool operator!=(const WireId &other) const { return tile != other.tile || index != other.index; } + bool operator<(const WireId &other) const + { + return tile < other.tile || (tile == other.tile && index < other.index); + } +}; + +struct PipId +{ + int32_t tile = -1; + int32_t index = -1; + + bool operator==(const PipId &other) const { return tile == other.tile && index == other.index; } + bool operator!=(const PipId &other) const { return tile != other.tile || index != other.index; } + bool operator<(const PipId &other) const + { + return tile < other.tile || (tile == other.tile && index < other.index); + } +}; + +struct GroupId +{ +}; + +struct DecalId +{ +}; + +struct ArchNetInfo +{ +}; + +struct NetInfo +{ +}; + +struct ArchCellInfo +{ +}; diff --git a/fpga_interchange/main.cc b/fpga_interchange/main.cc new file mode 100644 index 00000000..a2eab572 --- /dev/null +++ b/fpga_interchange/main.cc @@ -0,0 +1,85 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * + * 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. + * + */ + +#ifdef MAIN_EXECUTABLE + +#include +#include "command.h" +#include "design_utils.h" +#include "jsonwrite.h" +#include "log.h" +#include "timing.h" + +USING_NEXTPNR_NAMESPACE + +class FpgaInterchangeCommandHandler : public CommandHandler +{ + public: + FpgaInterchangeCommandHandler(int argc, char **argv); + virtual ~FpgaInterchangeCommandHandler(){}; + std::unique_ptr createContext(std::unordered_map &values) override; + void setupArchContext(Context *ctx) override{}; + void customBitstream(Context *ctx) override; + void customAfterLoad(Context *ctx) override; + + protected: + po::options_description getArchOptions() override; +}; + +FpgaInterchangeCommandHandler::FpgaInterchangeCommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {} + +po::options_description FpgaInterchangeCommandHandler::getArchOptions() +{ + po::options_description specific("Architecture specific options"); + specific.add_options()("chipdb", po::value(), "name of chip database binary"); + specific.add_options()("xdc", po::value>(), "XDC-style constraints file"); + specific.add_options()("phys", po::value(), "FPGA interchange Physical netlist to write"); + + return specific; +} + +void FpgaInterchangeCommandHandler::customBitstream(Context *ctx) +{ + if (vm.count("phys")) { + std::string filename = vm["phys"].as(); + ctx->writePhysicalNetlist(filename); + } +} + +std::unique_ptr FpgaInterchangeCommandHandler::createContext(std::unordered_map &values) +{ + ArchArgs chipArgs; + if (!vm.count("chipdb")) { + log_error("chip database binary must be provided\n"); + } + chipArgs.chipdb = vm["chipdb"].as(); + return std::unique_ptr(new Context(chipArgs)); +} + +void FpgaInterchangeCommandHandler::customAfterLoad(Context *ctx) +{ +} + +int main(int argc, char *argv[]) +{ + FpgaInterchangeCommandHandler handler(argc, argv); + return handler.exec(); +} + +#endif -- cgit v1.2.3