diff --git a/CMakeLists.txt b/CMakeLists.txt index 92b5d180..a95223a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ if (EXTERNAL_CHIPDB) endif() # List of families to build -set(FAMILIES generic ice40 ecp5) +set(FAMILIES generic ice40 ecp5 leuctra) set(ARCH "" CACHE STRING "Architecture family for nextpnr build") set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES}) @@ -67,6 +67,11 @@ foreach(item ${ARCH}) if (NOT item IN_LIST FAMILIES) message(FATAL_ERROR "Architecture '${item}' not in list of supported architectures") endif() + if (item STREQUAL "leuctra") + if (NOT EXTERNAL_CHIPDB) + message(FATAL_ERROR "The leuctra backend requires setting -DEXTERNAL_CHIPDB=ON") + endif() + endif() endforeach() set(CMAKE_CXX_STANDARD 11) diff --git a/gui/designwidget.cc b/gui/designwidget.cc index a0974ea8..92539334 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -313,7 +313,7 @@ void DesignWidget::newContext(Context *ctx) wireMap[std::pair(wire->x, wire->y)].push_back(wireid); } #endif -#ifdef ARCH_ECP5 +#if defined(ARCH_ECP5) || defined(ARCH_LEUCTRA) for (const auto &wire : ctx->getWires()) { wireMap[std::pair(wire.location.x, wire.location.y)].push_back(wire); } diff --git a/gui/leuctra/family.cmake b/gui/leuctra/family.cmake new file mode 100644 index 00000000..e69de29b diff --git a/gui/leuctra/mainwindow.cc b/gui/leuctra/mainwindow.cc new file mode 100644 index 00000000..dbd5cc7c --- /dev/null +++ b/gui/leuctra/mainwindow.cc @@ -0,0 +1,51 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 "mainwindow.h" + +static void initMainResource() { Q_INIT_RESOURCE(nextpnr); } + +NEXTPNR_NAMESPACE_BEGIN + +MainWindow::MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent) + : BaseMainWindow(std::move(context), args, parent) +{ + initMainResource(); + + std::string title = "nextpnr-leuctra - [EMPTY]"; + setWindowTitle(title.c_str()); + + connect(this, &BaseMainWindow::contextChanged, this, &MainWindow::newContext); + + createMenu(); +} + +MainWindow::~MainWindow() {} + +void MainWindow::newContext(Context *ctx) +{ + std::string title = "nextpnr-leuctra - " + ctx->getChipName(); + setWindowTitle(title.c_str()); +} + +void MainWindow::createMenu() {} + +void MainWindow::new_proj() {} + +NEXTPNR_NAMESPACE_END diff --git a/gui/leuctra/mainwindow.h b/gui/leuctra/mainwindow.h new file mode 100644 index 00000000..bb6a4cf1 --- /dev/null +++ b/gui/leuctra/mainwindow.h @@ -0,0 +1,45 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 MAINWINDOW_H +#define MAINWINDOW_H + +#include "../basewindow.h" + +NEXTPNR_NAMESPACE_BEGIN + +class MainWindow : public BaseMainWindow +{ + Q_OBJECT + + public: + explicit MainWindow(std::unique_ptr context, ArchArgs args, QWidget *parent = 0); + virtual ~MainWindow(); + + public: + void createMenu(); + + protected Q_SLOTS: + void new_proj() override; + void newContext(Context *ctx); +}; + +NEXTPNR_NAMESPACE_END + +#endif // MAINWINDOW_H diff --git a/gui/leuctra/nextpnr.qrc b/gui/leuctra/nextpnr.qrc new file mode 100644 index 00000000..03585ec0 --- /dev/null +++ b/gui/leuctra/nextpnr.qrc @@ -0,0 +1,2 @@ + + diff --git a/leuctra/arch.cc b/leuctra/arch.cc new file mode 100644 index 00000000..c810e68a --- /dev/null +++ b/leuctra/arch.cc @@ -0,0 +1,556 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 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 "nextpnr.h" +#include "placer1.h" +#include "router1.h" + +NEXTPNR_NAMESPACE_BEGIN + +static std::tuple split_identifier_name(const std::string &name) +{ + size_t first_slash = name.find('/'); + NPNR_ASSERT(first_slash != std::string::npos); + size_t second_slash = name.find('/', first_slash + 1); + NPNR_ASSERT(second_slash != std::string::npos); + return std::make_tuple(std::stoi(name.substr(1, first_slash)), + std::stoi(name.substr(first_slash + 2, second_slash - first_slash)), + name.substr(second_slash + 1)); +}; + +// ----------------------------------------------------------------------- + +void IdString::initialize_arch(const BaseCtx *ctx) +{ + // Nothing here -- IdString is actually initialized in the constructor, + // because we need to have bba loaded. +} + +// Given a device name, figure out what family it belongs to. +static Arch::Family device_to_family(const std::string &device) +{ + size_t pos = 0; + + // Skip the prefix. + if (device.substr(0, 2) == "xc") + pos += 2; + else if (device.substr(0, 2) == "xa") + pos += 2; + else if (device.substr(0, 2) == "xq") + pos += 2; + else if (device.substr(0, 3) == "xqr") + pos += 3; + auto raw = device.substr(pos); + + if (raw.substr(0, 2) == "vu" || raw.substr(0, 2) == "ku") { + // Ultrascale or Ultrascale+ (needs to be checked before original Virtex). + if (raw.substr(raw.size() - 1) == "p") + return Arch::FAMILY_ULTRASCALE_PLUS; + return Arch::FAMILY_ULTRASCALE; + } else if (raw.substr(0, 2) == "zu") { + // Zynq Ultrascale+. + return Arch::FAMILY_ULTRASCALE_PLUS; + } else if (raw.substr(0, 1) == "7") { + // 7 Series. + return Arch::FAMILY_SERIES7; + } else if (raw.substr(0, 2) == "6s") { + return Arch::FAMILY_SPARTAN6; + } else if (raw.substr(0, 2) == "6v") { + return Arch::FAMILY_VIRTEX6; + } else if (raw.substr(0, 2) == "5v") { + return Arch::FAMILY_VIRTEX5; + } else if (raw.substr(0, 2) == "4v") { + return Arch::FAMILY_VIRTEX4; + } else if (raw.substr(0, 3) == "3sd") { + // Needs to be checked before other Spartan 3 variants. + return Arch::FAMILY_SPARTAN3ADSP; + } else if (raw.substr(0, 2) == "3s") { + // One of many Spartan 3 variants. + if (raw.substr(raw.size() - 1) == "e") + return Arch::FAMILY_SPARTAN3E; + if (raw.substr(raw.size() - 1) == "a") + return Arch::FAMILY_SPARTAN3A; + if (raw.substr(raw.size() - 2) == "an") + return Arch::FAMILY_SPARTAN3A; + return Arch::FAMILY_SPARTAN3; + } else if (raw.substr(0, 3) == "2vp") { + // Virtex 2 Pro. + return Arch::FAMILY_VIRTEX2P; + } else if (raw.substr(0, 2) == "2v") { + // Virtex 2. + return Arch::FAMILY_VIRTEX2; + } else if (raw.substr(0, 1) == "v" || raw.substr(0, 2) == "2s") { + // Virtex or Virtex E. + if (raw.substr(raw.size() - 1) == "e") + return Arch::FAMILY_VIRTEXE; + return Arch::FAMILY_VIRTEX; + } else if (raw.substr(0, 1) == "s") { + // Spartan or Spartan XL. + if (raw.size() >= 2 && raw.substr(raw.size() - 2) == "xl") + return Arch::FAMILY_SPARTANXL; + return Arch::FAMILY_XC4000E; + } else if (raw.substr(0, 2) == "40") { + // One of the xc4000 families. + if (raw.substr(raw.size() - 1) == "e") + return Arch::FAMILY_XC4000E; + if (raw.substr(raw.size() - 2) == "ex") + return Arch::FAMILY_XC4000EX; + if (raw.substr(raw.size() - 2) == "xl") + return Arch::FAMILY_XC4000EX; + if (raw.size() >= 3 && raw.substr(raw.size() - 3) == "xla") + return Arch::FAMILY_XC4000XLA; + if (raw.substr(raw.size() - 2) == "xv") + return Arch::FAMILY_XC4000XV; + } + log_error("Unknown device family.\n"); +} + +static std::string family_name(Arch::Family family) { + switch(family) { + case Arch::FAMILY_XC4000E: + return "xc4000e"; + case Arch::FAMILY_XC4000EX: + return "xc4000ex"; + case Arch::FAMILY_XC4000XLA: + return "xc4000xla"; + case Arch::FAMILY_XC4000XV: + return "xc4000xv"; + case Arch::FAMILY_SPARTANXL: + return "spartanxl"; + case Arch::FAMILY_VIRTEX: + return "virtex"; + case Arch::FAMILY_VIRTEXE: + return "virtexe"; + case Arch::FAMILY_VIRTEX2: + return "virtex2"; + case Arch::FAMILY_VIRTEX2P: + return "virtex2p"; + case Arch::FAMILY_SPARTAN3: + return "spartan3"; + case Arch::FAMILY_SPARTAN3E: + return "spartan3e"; + case Arch::FAMILY_SPARTAN3A: + return "spartan3a"; + case Arch::FAMILY_SPARTAN3ADSP: + return "spartan3adsp"; + case Arch::FAMILY_VIRTEX4: + return "virtex4"; + case Arch::FAMILY_VIRTEX5: + return "virtex5"; + case Arch::FAMILY_VIRTEX6: + return "virtex6"; + case Arch::FAMILY_SPARTAN6: + return "spartan6"; + case Arch::FAMILY_SERIES7: + return "series7"; + case Arch::FAMILY_ULTRASCALE: + return "ultrascale"; + case Arch::FAMILY_ULTRASCALE_PLUS: + return "ultrascaleplus"; + } + NPNR_ASSERT_FALSE("strange family"); + return ""; +} + +Arch::Arch(ArchArgs args) : args(args) +{ + // Select and load family bba. + family = device_to_family(args.device); + std::string fname = family_name(family); + std::string family_filename = EXTERNAL_CHIPDB_ROOT "/leuctra/" + fname + ".bin"; + try { + mmap.open(family_filename); + if (!mmap.is_open()) + log_error("Unable to read chipdb %s\n", family_filename.c_str()); + } catch (...) { + log_error("Unable to read chipdb %s\n", family_filename.c_str()); + } + family_info = reinterpret_cast(mmap.data()); + if (family_info->format_tag != DB_FORMAT_TAG_CURRENT) + log_error("Chipdb %s has wrong format tag\n", family_filename.c_str()); + + // Slurp IdStrings. + // Entry 0 must be "". + NPNR_ASSERT(family_info->idstrings[0].get()[0] == 0); + for (int i = 1; i < family_info->num_idstrings; i++) + IdString::initialize_add(this, family_info->idstrings[i].get(), i); + + // Make double sure we got the right family. + if (IdString(family_info->name_id).str(this) != fname) + log_error("Chipdb %s is for strange family\n", family_filename.c_str()); + + // Search for the device. + int dev_name_id = id(args.device).index; + for (int i = 0; i < family_info->num_devices; i++) { + if (family_info->devices[i].name_id == dev_name_id) { + device_info = family_info->devices[i].device.get(); + break; + } + } + if (device_info == nullptr) + log_error("Unknown device.\n"); + + // Find the right package. + int pkg_name_id = id(args.package).index; + if (pkg_name_id == 0) { + package_info = &device_info->packages[0]; + } else { + for (int i = 0; i < device_info->num_packages; i++) { + if (device_info->packages[i].name_id == pkg_name_id) { + package_info = &device_info->packages[i]; + break; + } + } + } + if (package_info == nullptr) + log_error("Unknown package.\n"); +} + +// ----------------------------------------------------------------------- + +BelId Arch::getBelByName(IdString name) const +{ + BelId ret; + auto it = bel_by_name.find(name); + if (it != bel_by_name.end()) + return it->second; + + Location loc; + std::string basename; + std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this)); + ret.location = loc; + IdString basename_id = id(basename); + auto &tt = getTileType(loc.x, loc.y); + for (int i = 0; i < tt.num_bels; i++) { + if (tt.bels[i].name_id == basename_id.index) { + ret.index = i; + bel_by_name[name] = ret; + return ret; + } + } + return BelId(); +} + +WireId Arch::getBelPinWire(BelId bel, IdString pin) const +{ + WireId ret; + + NPNR_ASSERT(bel != BelId()); + + int num_bel_wires = getBelTypeInfo(bel).num_pins; + const BelTypePinPOD *bel_type_wires = getBelTypeInfo(bel).pins.get(); + for (int i = 0; i < num_bel_wires; i++) + if (bel_type_wires[i].name_id == pin.index) { + ret.location = bel.location; + ret.index = getTileTypeBel(bel).pin_wires[i]; + break; + } + + return ret; +} + +PortType Arch::getBelPinType(BelId bel, IdString pin) const +{ + NPNR_ASSERT(bel != BelId()); + + int num_bel_wires = getBelTypeInfo(bel).num_pins; + const BelTypePinPOD *bel_type_wires = getBelTypeInfo(bel).pins.get(); + for (int i = 0; i < num_bel_wires; i++) + if (bel_type_wires[i].name_id == pin.index) { + bool is_in = bel_type_wires[i].flags & BelTypePinPOD::FLAG_INPUT; + bool is_out = bel_type_wires[i].flags & BelTypePinPOD::FLAG_OUTPUT; + if (is_in && is_out) + return PORT_INOUT; + if (is_in) + return PORT_IN; + assert(is_out); + return PORT_OUT; + } + + return PORT_INOUT; +} + +// ----------------------------------------------------------------------- + +WireId Arch::getWireByName(IdString name) const +{ + WireId ret; + auto it = wire_by_name.find(name); + if (it != wire_by_name.end()) + return it->second; + + Location loc; + std::string basename; + std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this)); + ret.location = loc; + IdString basename_id = id(basename); + auto &tt = getTileType(loc.x, loc.y); + for (int i = 0; i < tt.num_wires; i++) { + if (tt.wires[i].name_id == basename_id.index) { + ret.index = i; + wire_by_name[name] = ret; + return ret; + } + } + return WireId(); +} + +// ----------------------------------------------------------------------- + +PipId Arch::getPipByName(IdString name) const +{ + auto it = pip_by_name.find(name); + if (it != pip_by_name.end()) + return it->second; + + PipId ret; + Location loc; + std::string basename; + std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this)); + ret.location = loc; + AllPipRange range; + range.b.cursor_tile = loc.x + device_info->width * loc.y; + range.b.cursor_kind = PIP_KIND_PIP; + range.b.cursor_index = 0; + range.b.cursor_subindex = -1; + range.b.device = device_info; + range.b.family = family_info; + ++range.b; + range.e.cursor_tile = loc.x + device_info->width * loc.y + 1; + range.e.cursor_kind = PIP_KIND_PIP; + range.e.cursor_index = 0; + range.e.cursor_subindex = -1; + range.e.device = device_info; + range.e.family = family_info; + ++range.e; + for (const auto& curr: range) { + 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(this)); + return pip_by_name[name]; +} + +IdString Arch::getPipName(PipId pip) const +{ + NPNR_ASSERT(pip != PipId()); + + int x = pip.location.x; + int y = pip.location.y; + + if (pip.kind == PIP_KIND_PIP) { + std::string src_name = getWireBasename(getPipSrcWire(pip)).str(this); + std::string dst_name = getWireBasename(getPipDstWire(pip)).str(this); + return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name); + } else { + auto &tt = getTileType(pip.location); + std::string port_name = IdString(tt.ports[pip.index].name_id).str(this); + std::string dst_name = getWireBasename(getPipDstWire(pip)).str(this); + return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + port_name + "/" + std::to_string(pip.subindex) + ".->." + dst_name); + } +} + +// ----------------------------------------------------------------------- + +void PipIterator::operator++() { + cursor_index++; + auto &ttw = arch->getTileTypeWire(wire); + if (stage == STAGE_PIPS) { + int num; + if (mode == MODE_UPHILL) + num = ttw.num_pip_dst_xrefs; + else + num = ttw.num_pip_src_xrefs; + if (cursor_index == num) { + cursor_index = 0; + stage = STAGE_PORTS; + } + } + if (stage == STAGE_PORTS) { + while (true) { + if (cursor_index == ttw.num_port_xrefs) { + cursor_index = 0; + stage = STAGE_END; + break; + } + // Make sure the port is connected. + auto &tile = arch->getTile(wire.location); + auto &xref = ttw.port_xrefs[cursor_index]; + auto &conn = tile.conns[xref.port_idx]; + if (conn.port_idx != -1) { + // Make sure the wire in a port is connected. + Location other_loc; + other_loc.x = conn.tile_x; + other_loc.y = conn.tile_y; + auto &other_tt = arch->getTileType(other_loc); + if (other_tt.ports[conn.port_idx].wires[xref.wire_idx] != -1) + break; + } + cursor_index++; + } + } +} + +PipId PipIterator::operator*() const { + PipId ret; + auto &ttw = arch->getTileTypeWire(wire); + ret.location = wire.location; + if (mode == MODE_UPHILL) { + if (stage == STAGE_PIPS) { + ret.kind = PIP_KIND_PIP; + ret.index = ttw.pip_dst_xrefs[cursor_index]; + } else { + ret.kind = PIP_KIND_PORT; + auto &xref = ttw.port_xrefs[cursor_index]; + ret.index = xref.port_idx; + ret.subindex = xref.wire_idx; + } + } else { + if (stage == STAGE_PIPS) { + ret.kind = PIP_KIND_PIP; + ret.index = ttw.pip_src_xrefs[cursor_index]; + } else { + ret.kind = PIP_KIND_PORT; + auto &tile = arch->getTile(wire.location); + auto &xref = ttw.port_xrefs[cursor_index]; + auto &conn = tile.conns[xref.port_idx]; + ret.location.x = conn.tile_x; + ret.location.y = conn.tile_y; + ret.index = conn.port_idx; + ret.subindex = xref.wire_idx; + } + } + return ret; +} + +// ----------------------------------------------------------------------- +// +// XXX package pins + +std::vector Arch::getBelPins(BelId bel) const +{ + std::vector ret; + NPNR_ASSERT(bel != BelId()); + + int num_bel_wires = getBelTypeInfo(bel).num_pins; + const BelTypePinPOD *bel_type_wires = getBelTypeInfo(bel).pins.get(); + + for (int i = 0; i < num_bel_wires; i++) { + IdString id; + id.index = bel_type_wires[i].name_id; + ret.push_back(id); + } + + return ret; +} + +// ----------------------------------------------------------------------- + +delay_t Arch::estimateDelay(WireId src, WireId dst) const +{ + // TODO + return 13; +} + +delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const +{ + // TODO + return 13; +} + +bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } + + +// ----------------------------------------------------------------------- + +bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); } + +bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); } + +// ----------------------------------------------------------------------- + +std::vector Arch::getDecalGraphics(DecalId decal) const +{ + std::vector ret; + + if (decal.type == DecalId::TYPE_BEL) { + BelId bel; + bel.index = decal.z; + bel.location = decal.location; + int max_z = getTileBelDimZ(bel.location.x, bel.location.y); + int z = bel.index; + //auto bel_type = getBelType(bel); + + GraphicElement el; + el.type = GraphicElement::TYPE_BOX; + el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE; + el.x1 = bel.location.x + 0.05; + el.x2 = bel.location.x + 0.95; + el.y1 = bel.location.y + (z + 0.05) / max_z; + el.y2 = bel.location.y + (z + 0.95) / max_z; + ret.push_back(el); + } + + return ret; +} + +DecalXY Arch::getBelDecal(BelId bel) const +{ + DecalXY decalxy; + decalxy.decal.type = DecalId::TYPE_BEL; + decalxy.decal.location = bel.location; + decalxy.decal.z = bel.index; + decalxy.decal.active = !checkBelAvail(bel); + return decalxy; +} + +DecalXY Arch::getWireDecal(WireId wire) const { return {}; } + +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 +{ + // XXX + delay.min_delay = 11; + delay.max_delay = 13; + return true; +} + +TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const +{ + // XXX + return TMG_IGNORE; +} + +TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const +{ + TimingClockingInfo info; + info.setup = getDelayFromNS(0); + info.hold = getDelayFromNS(0); + info.clockToQ = getDelayFromNS(0); + // XXX + return info; +} + +NEXTPNR_NAMESPACE_END diff --git a/leuctra/arch.h b/leuctra/arch.h new file mode 100644 index 00000000..bfce158a --- /dev/null +++ b/leuctra/arch.h @@ -0,0 +1,1182 @@ +/* + * 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 "arch.h" via "nextpnr.h" only. +#endif + +#include + +NEXTPNR_NAMESPACE_BEGIN + +/**** Everything in this section must be kept in sync with leuctra/make_bba.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) + offset); } + + const T &operator[](size_t index) const { return get()[index]; } + + const T &operator*() const { return *(get()); } + + const T *operator->() const { return get(); } +}; + +/************************ Per-device structures. ************************/ + +NPNR_PACKED_STRUCT(struct PortIdPOD { + int16_t tile_x; + int16_t tile_y; + int32_t port_idx; +}); + +NPNR_PACKED_STRUCT(struct BelIdPOD { + int16_t tile_x; + int16_t tile_y; + int32_t bel_idx; +}); + +// Represents a single bel on a device. +NPNR_PACKED_STRUCT(struct BelPOD { + enum BelFlags : uint32_t { + // Differential positive / negative terminal. + FLAG_IO_DIFF_P = 0x1, + FLAG_IO_DIFF_N = 0x2, + // IOB without output buffer (Spartan 3E, 3A). + FLAG_IO_INPUT_ONLY = 0x4, + // Left or right IOB (Spartan 3A). + FLAR_IO_LR = 0x8, + // Low-capacitance IOB (Virtex 4). + FLAG_IO_LOWCAP = 0x10, + // High Performance vs High Range IO (Series 7) -- applies to IOBs, [IO]LOGIC, [IO]DELAY. + FLAG_IO_HP = 0x20, + FLAG_IO_HR = 0x40, + // VREF pad (cannot be used if VREF IO standard used in the same bank). + FLAG_IO_VREF = 0x80, + // Positive / negative DCI calibration pad (cannot be used if DCI IO standard used in the same bank). + FLAG_IO_VP = 0x100, + FLAG_IO_VN = 0x200, + // Multi-function pin used by the configuration interface (cannot be used if Persist option given). + FLAG_IO_PERSIST = 0x400, + // Subtype for slices. + FLAG_SLICEX = 0x800, + FLAG_SLICEL = 0x1000, + FLAG_SLICEM = 0x2000, + // For Virtex 6 and Series 7 18-kbit RAM: set if this bel can fit a FIFO18E1. + FLAG_FIFO = 0x4000, + }; + int32_t io_bank; + BelFlags flags; + RelPtr related; + RelPtr conflicts; +}); + +// Represents a single tile on a device. +NPNR_PACKED_STRUCT(struct TilePOD { + int32_t tile_type_idx; + RelPtr bels; + RelPtr conns; +}); + +NPNR_PACKED_STRUCT(struct PackagePinPOD { + int32_t name_id; + BelIdPOD bel; +}); + +NPNR_PACKED_STRUCT(struct PackageInfoPOD { + int32_t name_id; + int32_t num_pins; + RelPtr pin_data; +}); + +// Represents a single device die. +NPNR_PACKED_STRUCT(struct DevicePOD { + // Width and height in tiles. + int16_t width; + int16_t height; + RelPtr tiles; + int32_t num_packages; + RelPtr packages; +}); + +/************************ Per-family structures. ************************/ + +// Represents a BEL type or cell type pin. +NPNR_PACKED_STRUCT(struct BelTypePinPOD { + enum BelTypePinFlags : uint32_t { + // Both can be set for an inout pin. + FLAG_INPUT = 0x1, + FLAG_OUTPUT = 0x2, + FLAG_CLOCK = 0x4, + FLAG_INVERTIBLE = 0x8, + // Participates in global interconnect (all global outputs can reach + // all global inputs). + FLAG_ROUTE_GLOBAL = 0x10, + // Drives or can be driven from the clock interconnect. + FLAG_ROUTE_CLOCK = 0x20, + // Has dedicated routing. + FLAG_ROUTE_DEDICATED = 0x40, + }; + int32_t name_id; + BelTypePinFlags flags; +}); + +// Represents a type of BEL available in a given family. May fit several cell types. +NPNR_PACKED_STRUCT(struct BelTypePOD { + enum BelTypeFlags : uint32_t { + // This BEL type is associated with a physical pad on the die. + FLAG_HAS_PAD = 0x1, + FLAG_IS_GLOBAL_BUF = 0x2, + }; + int32_t name_id; + BelTypeFlags flags; + int32_t num_pins; + RelPtr pins; + int32_t num_related; + RelPtr related_name_ids; + int32_t num_conflicts; +}); + +NPNR_PACKED_STRUCT(struct TileTypeWirePortXrefPOD { + int32_t port_idx; + int32_t wire_idx; +}); + +// Represents a wire in a tile type. +NPNR_PACKED_STRUCT(struct TileTypeWirePOD { + int32_t name_id; + int32_t type_name_id; + // The BEL and pin this wire is attached to, if any. + int32_t bel_idx; + int32_t bel_pin_idx; + // A list of pips referencing this wire as dst. + int32_t num_pip_dst_xrefs; + RelPtr pip_dst_xrefs; + // A list of pips referencing this wire as src. + int32_t num_pip_src_xrefs; + RelPtr pip_src_xrefs; + // A list of ports referencing this wire. + int32_t num_port_xrefs; + RelPtr port_xrefs; +}); + +// Represents a bel in a tile type. +NPNR_PACKED_STRUCT(struct TileTypeBelPOD { + int32_t bel_type_idx; + int32_t name_id; + RelPtr pin_wires; +}); + +// Represents a pip in a tile type. +NPNR_PACKED_STRUCT(struct TileTypePipPOD { + enum TileTypePipFlags : uint32_t { + FLAG_ALWAYS_ON = 0x1, + FLAG_THROUGH_BEL = 0x2, + }; + TileTypePipFlags flags; + int32_t wire_src; + int32_t wire_dst; + int32_t bel_through; +}); + +// Represents a port in a tile type. +NPNR_PACKED_STRUCT(struct TileTypePortPOD { + enum TileTypePortFlags : uint32_t { + FLAG_DIR_S = 0x1, + FLAG_DIR_N = 0x2, + FLAG_DIR_E = 0x4, + FLAG_DIR_W = 0x8, + }; + TileTypePortFlags flags; + int32_t name_id; + int32_t num_wires; + RelPtr wires; +}); + +// Represents a tile type available in a given family. +NPNR_PACKED_STRUCT(struct TileTypePOD { + enum TileTypeFlags : uint32_t { + // This tile type is empty space and shouldn't be drawn in GUI. + FLAG_EMPTY = 0x1, + // This tile type is a full node in the main interconnect grid. + FLAG_INT = 0x2, + }; + TileTypeFlags flags; + int32_t name_id; + // How many extra grid slots this tile type takes up (for GUI). + // The extra slots should be filled with FLAG_EMPTY tiles in the db. + // A normal unit tile has 0 in all 4 fields. + int16_t extend_up; + int16_t extend_down; + int16_t extend_left; + int16_t extend_right; + int32_t num_wires; + // Sorted by name_id. + RelPtr wires; + int32_t num_bels; + // Sorted by name_id. + RelPtr bels; + int32_t num_pips; + RelPtr pips; + int32_t num_ports; + RelPtr ports; +}); + +NPNR_PACKED_STRUCT(struct DeviceCatalogueEntryPOD { + // Device name (what the user selects). + int32_t name_id; + RelPtr device; +}); + +NPNR_PACKED_STRUCT(struct FamilyPOD { + // Must be equal to DB_FORMAT_TAG_CURRENT, used to identify db format revision. + uint32_t format_tag; + // Family name. + int32_t name_id; + // Devices in this family (to be loaded from separate bbas). + int32_t num_devices; + RelPtr devices; + // The initial IdString mapping. + int32_t num_idstrings; + RelPtr> idstrings; + // A description of available bel types. + int32_t num_bel_types; + RelPtr bel_types; + // A descripion of available tile types. + int32_t num_tile_types; + RelPtr tile_types; +}); + +#define DB_FORMAT_TAG_CURRENT 0x2 + +/************************ End of chipdb section. ************************/ + +struct BelIterator +{ + const DevicePOD *device; + const FamilyPOD *family; + int cursor_index; + int cursor_tile; + + BelIterator operator++() + { + cursor_index++; + while (cursor_tile < device->width * device->height && + cursor_index >= family->tile_types[device->tiles[cursor_tile].tile_type_idx].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.location.x = cursor_tile % device->width; + ret.location.y = cursor_tile / device->width; + ret.index = cursor_index; + return ret; + } +}; + +struct BelRange +{ + BelIterator b, e; + BelIterator begin() const { return b; } + BelIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + +struct BelPinIterator +{ + BelId bel; + IdString pin; + + void operator++() { bel = BelId(); } + bool operator!=(const BelPinIterator &other) const { return bel != other.bel; } + + BelPin operator*() const + { + BelPin ret; + ret.bel = bel; + ret.pin = pin; + return ret; + } +}; + +struct BelPinRange +{ + BelPinIterator b, e; + BelPinIterator begin() const { return b; } + BelPinIterator end() const { return e; } +}; + + +// ----------------------------------------------------------------------- + +struct WireIterator +{ + const DevicePOD *device; + const FamilyPOD *family; + int cursor_index; + int cursor_tile; + + WireIterator operator++() + { + cursor_index++; + while (cursor_tile < device->width * device->height && + cursor_index >= family->tile_types[device->tiles[cursor_tile].tile_type_idx].num_wires) { + cursor_index = 0; + cursor_tile++; + } + 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.location.x = cursor_tile % device->width; + ret.location.y = cursor_tile / device->width; + ret.index = cursor_index; + return ret; + } +}; + +struct WireRange +{ + WireIterator b, e; + WireIterator begin() const { return b; } + WireIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + +struct AllPipIterator +{ + const DevicePOD *device; + const FamilyPOD *family; + int cursor_tile; + PipKind cursor_kind; + int cursor_index; + int cursor_subindex; + + AllPipIterator operator++() + { + cursor_subindex++; + while (true) { + auto &tt = family->tile_types[device->tiles[cursor_tile].tile_type_idx]; + if (cursor_tile >= device->width * device->height) + break; + if (cursor_kind == PIP_KIND_PIP) { + if (cursor_subindex != 0) { + cursor_subindex = 0; + cursor_index++; + } + if (cursor_index >= tt.num_pips) { + cursor_kind = PIP_KIND_PORT; + cursor_index = 0; + } + } + if (cursor_kind == PIP_KIND_PORT) { + if (cursor_index >= tt.num_ports) { + cursor_kind = PIP_KIND_PIP; + cursor_index = 0; + cursor_tile++; + continue; + } + // Skip unconnected ports. + auto &conn = device->tiles[cursor_tile].conns[cursor_index]; + if (conn.port_idx == -1) { + cursor_index++; + continue; + } + if (cursor_subindex >= tt.ports[cursor_index].num_wires) { + cursor_subindex = 0; + cursor_index++; + continue; + } + // Skip unconnected wires in a port. + if (tt.ports[cursor_index].wires[cursor_subindex] == -1) { + cursor_subindex++; + continue; + } + auto &other_tile = device->tiles[conn.tile_x + device->width * conn.tile_y]; + auto &other_tt = family->tile_types[other_tile.tile_type_idx]; + if (other_tt.ports[conn.port_idx].wires[cursor_subindex] == -1) { + cursor_subindex++; + continue; + } + } + break; + } + return *this; + } + AllPipIterator operator++(int) + { + AllPipIterator prior(*this); + ++(*this); + return prior; + } + + bool operator!=(const AllPipIterator &other) const + { + return cursor_subindex != other.cursor_subindex || cursor_index != other.cursor_index || cursor_kind != other.cursor_kind || cursor_tile != other.cursor_tile; + } + + bool operator==(const AllPipIterator &other) const + { + return cursor_subindex == other.cursor_subindex && cursor_index == other.cursor_index && cursor_kind == other.cursor_kind && cursor_tile == other.cursor_tile; + } + + PipId operator*() const + { + PipId ret; + ret.location.x = cursor_tile % device->width; + ret.location.y = cursor_tile / device->width; + ret.kind = cursor_kind; + ret.index = cursor_index; + ret.subindex = cursor_subindex; + return ret; + } +}; + +struct AllPipRange +{ + AllPipIterator b, e; + AllPipIterator begin() const { return b; } + AllPipIterator end() const { return e; } +}; + +// ----------------------------------------------------------------------- + +struct Arch; + +struct PipIterator +{ + enum { + STAGE_PIPS, + STAGE_PORTS, + STAGE_END, + } stage; + enum { + MODE_DOWNHILL, + MODE_UPHILL, + } mode; + int cursor_index; + WireId wire; + const Arch *arch; + + void operator++(); + + bool operator!=(const PipIterator &other) const { return stage != other.stage || cursor_index != other.cursor_index; } + + PipId operator*() const; +}; + +struct PipRange +{ + PipIterator b, e; + PipIterator begin() const { return b; } + PipIterator end() const { return e; } +}; + + +struct ArchArgs +{ + std::string device; + std::string package; + std::string speed; +}; + +struct Arch : BaseCtx +{ + enum Family { + FAMILY_XC4000E, // Also known as Spartan. + FAMILY_XC4000EX, // Also xc4000xl. + FAMILY_XC4000XLA, + FAMILY_XC4000XV, + FAMILY_SPARTANXL, + FAMILY_VIRTEX, // Also known as Spartan 2. + FAMILY_VIRTEXE, // Also known as Spartan 2E. + FAMILY_VIRTEX2, + FAMILY_VIRTEX2P, + FAMILY_SPARTAN3, + FAMILY_SPARTAN3E, + FAMILY_SPARTAN3A, + FAMILY_SPARTAN3ADSP, + FAMILY_VIRTEX4, + FAMILY_VIRTEX5, + FAMILY_VIRTEX6, + FAMILY_SPARTAN6, + FAMILY_SERIES7, + FAMILY_ULTRASCALE, + FAMILY_ULTRASCALE_PLUS, + } family; + + boost::iostreams::mapped_file_source mmap; + + const FamilyPOD *family_info; + const DevicePOD *device_info; + const PackageInfoPOD *package_info; + + mutable std::unordered_map bel_by_name; + mutable std::unordered_map wire_by_name; + mutable std::unordered_map pip_by_name; + + std::unordered_map bel_to_cell; + std::unordered_map wire_to_net; + std::unordered_map pip_to_net; + + ArchArgs args; + Arch(ArchArgs args); + + std::string getChipName() const + { + return args.device; + } + + IdString archId() const { return id("leuctra"); } + ArchArgs archArgs() const { return args; } + IdString archArgsToId(ArchArgs args) { return id(args.device); } + + const TilePOD &getTile(int x, int y) const + { + return device_info->tiles[x + device_info->width * y]; + } + + const TilePOD &getTile(Location loc) const + { + return getTile(loc.x, loc.y); + } + + const TileTypePOD &getTileType(int x, int y) const + { + return family_info->tile_types[getTile(x, y).tile_type_idx]; + } + + const TileTypePOD &getTileType(Location loc) const + { + return getTileType(loc.x, loc.y); + } + + const TileTypeBelPOD &getTileTypeBel(BelId bel) const + { + return getTileType(bel.location).bels[bel.index]; + } + + const TileTypeWirePOD &getTileTypeWire(WireId wire) const + { + return getTileType(wire.location).wires[wire.index]; + } + + const BelTypePOD &getBelTypeInfo(BelId bel) const + { + return family_info->bel_types[getTileTypeBel(bel).bel_type_idx]; + } + + // ------------------------------------------------- + + int getGridDimX() const { return device_info->width; }; + int getGridDimY() const { return device_info->height; }; + int getTileBelDimZ(int x, int y) const + { + return getTileType(x, y).num_bels; + }; + int getTilePipDimZ(int, int) const { return 1; }; + + // ------------------------------------------------- + + BelId getBelByName(IdString name) const; + + IdString getBelName(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + std::stringstream name; + name << "X" << bel.location.x << "/Y" << bel.location.y << "/" << IdString(getTileTypeBel(bel).name_id).str(this); + return id(name.str()); + } + + uint32_t getBelChecksum(BelId bel) const { return bel.index; } + + void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) + { + NPNR_ASSERT(bel != BelId()); + NPNR_ASSERT(bel_to_cell[bel] == nullptr); + bel_to_cell[bel] = cell; + cell->bel = bel; + cell->belStrength = strength; + refreshUiBel(bel); + } + + void unbindBel(BelId bel) + { + NPNR_ASSERT(bel != BelId()); + NPNR_ASSERT(bel_to_cell[bel] != nullptr); + bel_to_cell[bel]->bel = BelId(); + bel_to_cell[bel]->belStrength = STRENGTH_NONE; + bel_to_cell[bel] = nullptr; + refreshUiBel(bel); + } + + Loc getBelLocation(BelId bel) const + { + Loc loc; + loc.x = bel.location.x; + loc.y = bel.location.y; + loc.z = bel.index; + return loc; + } + + BelId getBelByLocation(Loc loc) const + { + BelId res; + res.location.x = loc.x; + res.location.y = loc.y; + res.index = loc.z; + return res; + } + + BelRange getBelsByTile(int x, int y) const + { + BelRange range; + range.b.cursor_tile = x + y * device_info->width; + range.b.cursor_index = -1; + range.b.device = device_info; + range.b.family = family_info; + ++range.b; + range.e.cursor_tile = x + y * device_info->width; + range.e.cursor_index = getTileType(x, y).num_bels - 1; + range.e.device = device_info; + range.e.family = family_info; + ++range.e; + return range; + } + + bool getBelGlobalBuf(BelId bel) const + { + return getBelTypeInfo(bel).flags & BelTypePOD::FLAG_IS_GLOBAL_BUF; + } + + bool checkBelAvail(BelId bel) const + { + return getConflictingBelCell(bel) == nullptr; + } + + CellInfo *getBoundBelCell(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + if (bel_to_cell.find(bel) == bel_to_cell.end()) + return nullptr; + else + return bel_to_cell.at(bel); + } + + CellInfo *getConflictingBelCell(BelId bel) const + { + NPNR_ASSERT(bel != BelId()); + CellInfo *ret = getBoundBelCell(bel); + if (ret != nullptr) + return ret; + auto &bt = getBelTypeInfo(bel); + auto &tile = getTile(bel.location); + for (int i = 0; i < bt.num_conflicts; i++) { + auto &other_pod = tile.bels[bel.index].conflicts[i]; + BelId other; + other.location.x = other_pod.tile_x; + other.location.y = other_pod.tile_y; + other.index = other_pod.bel_idx; + ret = getBoundBelCell(other); + if (ret != nullptr) + return ret; + } + return nullptr; + } + + BelRange getBels() const + { + BelRange range; + range.b.cursor_tile = 0; + range.b.cursor_index = -1; + range.b.device = device_info; + range.b.family = family_info; + ++range.b; //-1 and then ++ deals with the case of no Bels in the first tile + range.e.cursor_tile = device_info->width * device_info->height; + range.e.cursor_index = 0; + range.e.device = device_info; + range.e.family = family_info; + return range; + } + + IdString getBelType(BelId bel) const + { + return IdString(getBelTypeInfo(bel).name_id); + } + + std::vector> getBelAttrs(BelId) const + { + // TODO: dump IO attrs here. + std::vector> ret; + return ret; + } + + WireId getBelPinWire(BelId bel, IdString pin) const; + + BelPinRange getWireBelPins(WireId wire) const + { + BelPinRange res; + auto &ttw = getTileTypeWire(wire); + if (ttw.bel_idx != -1) { + res.b.bel.location = wire.location; + res.b.bel.index = ttw.bel_idx; + auto &bt = getBelTypeInfo(res.b.bel); + res.b.pin.index = bt.pins[ttw.bel_pin_idx].name_id; + } + return res; + } + + std::vector getBelPins(BelId bel) const; + + // ------------------------------------------------- + + WireId getWireByName(IdString name) const; + + IdString getWireName(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + std::stringstream name; + name << "X" << wire.location.x << "/Y" << wire.location.y << "/" << getWireBasename(wire).str(this); + return id(name.str()); + } + + IdString getWireType(WireId wire) const + { + return IdString(getTileTypeWire(wire).type_name_id); + } + + std::vector> getWireAttrs(WireId) const + { + std::vector> ret; + return ret; + } + + 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; + } + + 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; + } + + bool checkWireAvail(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + return wire_to_net.find(wire) == wire_to_net.end() || wire_to_net.at(wire) == nullptr; + } + + NetInfo *getBoundWireNet(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + if (wire_to_net.find(wire) == wire_to_net.end()) + return nullptr; + else + return wire_to_net.at(wire); + } + + WireId getConflictingWireWire(WireId wire) const { return wire; } + + NetInfo *getConflictingWireNet(WireId wire) const + { + NPNR_ASSERT(wire != WireId()); + if (wire_to_net.find(wire) == wire_to_net.end()) + return nullptr; + else + return wire_to_net.at(wire); + } + + DelayInfo getWireDelay(WireId wire) const + { + DelayInfo delay; + delay.min_delay = 0; + delay.max_delay = 0; + return delay; + } + + WireRange getWires() const + { + WireRange range; + range.b.cursor_tile = 0; + range.b.cursor_index = -1; + range.b.device = device_info; + range.b.family = family_info; + ++range.b; //-1 and then ++ deals with the case of no wries in the first tile + range.e.cursor_tile = device_info->width * device_info->height; + range.e.cursor_index = 0; + range.e.device = device_info; + range.e.family = family_info; + return range; + } + + IdString getWireBasename(WireId wire) const + { + return IdString(getTileTypeWire(wire).name_id); + } + + WireId getWireByLocAndBasename(Location loc, std::string basename) const + { + IdString basename_id = id(basename); + WireId wireId; + wireId.location = loc; + auto &tt = getTileType(loc); + for (int i = 0; i < tt.num_wires; i++) { + if (tt.wires[i].name_id == basename_id.index) { + wireId.index = i; + return wireId; + } + } + return WireId(); + } + + // ------------------------------------------------- + + PipId getPipByName(IdString name) const; + IdString getPipName(PipId pip) const; + + IdString getPipType(PipId pip) const + { + // TODO: more pip kinds? + if (pip.kind == PIP_KIND_PIP) + return id("pip"); + else + return id("port"); + } + + std::vector> getPipAttrs(PipId) const + { + std::vector> ret; + return ret; + } + + uint32_t getPipChecksum(PipId pip) const { return pip.index; } + + void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) + { + NPNR_ASSERT(pip != PipId()); + NPNR_ASSERT(pip_to_net[pip] == nullptr); + + pip_to_net[pip] = net; + + WireId dst = getPipDstWire(pip); + NPNR_ASSERT(wire_to_net[dst] == nullptr); + wire_to_net[dst] = net; + net->wires[dst].pip = pip; + net->wires[dst].strength = strength; + } + + void unbindPip(PipId pip) + { + NPNR_ASSERT(pip != PipId()); + NPNR_ASSERT(pip_to_net[pip] != nullptr); + + WireId dst = getPipDstWire(pip); + NPNR_ASSERT(wire_to_net[dst] != nullptr); + wire_to_net[dst] = nullptr; + pip_to_net[pip]->wires.erase(dst); + + pip_to_net[pip] = nullptr; + } + + 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()); + if (pip_to_net.find(pip) == pip_to_net.end()) + return nullptr; + else + return pip_to_net.at(pip); + } + + WireId getConflictingPipWire(PipId pip) const { return WireId(); } + + NetInfo *getConflictingPipNet(PipId pip) const + { + NPNR_ASSERT(pip != PipId()); + if (pip_to_net.find(pip) == pip_to_net.end()) + return nullptr; + else + return pip_to_net.at(pip); + } + + AllPipRange getPips() const + { + AllPipRange range; + range.b.cursor_tile = 0; + range.b.cursor_kind = PIP_KIND_PIP; + range.b.cursor_index = 0; + range.b.cursor_subindex = -1; + range.b.device = device_info; + range.b.family = family_info; + ++range.b; //-1 and then ++ deals with the case of no wries in the first tile + range.e.cursor_tile = device_info->width * device_info->height; + range.e.cursor_kind = PIP_KIND_PIP; + range.e.cursor_index = 0; + range.e.cursor_subindex = 0; + range.e.device = device_info; + range.e.family = family_info; + return range; + } + + WireId getPipSrcWire(PipId pip) const + { + WireId wire; + auto &tt = getTileType(pip.location); + if (pip.kind == PIP_KIND_PIP) { + wire.location = pip.location; + wire.index = tt.pips[pip.index].wire_src; + } else { + auto &tile = getTile(pip.location); + auto &conn = tile.conns[pip.index]; + wire.location.x = conn.tile_x; + wire.location.y = conn.tile_y; + auto &other_tt = getTileType(wire.location); + wire.index = other_tt.ports[conn.port_idx].wires[pip.subindex]; + } + return wire; + } + + WireId getPipDstWire(PipId pip) const + { + WireId wire; + auto &tt = getTileType(pip.location); + if (pip.kind == PIP_KIND_PIP) { + wire.location = pip.location; + wire.index = tt.pips[pip.index].wire_dst; + } else { + wire.location = pip.location; + wire.index = tt.ports[pip.index].wires[pip.subindex]; + } + return wire; + } + + DelayInfo getPipDelay(PipId pip) const + { + // TODO: delays. + DelayInfo del; + del.min_delay = 11; + del.max_delay = 13; + return del; + } + + PipRange getPipsDownhill(WireId wire) const + { + PipRange ret; + ret.b.arch = this; + ret.b.wire = wire; + ret.b.mode = PipIterator::MODE_DOWNHILL; + ret.b.stage = PipIterator::STAGE_PIPS; + ret.b.cursor_index = -1; + ++ret.b; + ret.e.arch = this; + ret.e.wire = wire; + ret.e.mode = PipIterator::MODE_DOWNHILL; + ret.e.stage = PipIterator::STAGE_END; + ret.e.cursor_index = 0; + return ret; + } + + PipRange getPipsUphill(WireId wire) const + { + PipRange ret; + ret.b.arch = this; + ret.b.wire = wire; + ret.b.mode = PipIterator::MODE_UPHILL; + ret.b.stage = PipIterator::STAGE_PIPS; + ret.b.cursor_index = -1; + ++ret.b; + ret.e.arch = this; + ret.e.wire = wire; + ret.e.mode = PipIterator::MODE_UPHILL; + ret.e.stage = PipIterator::STAGE_END; + ret.e.cursor_index = 0; + return ret; + } + + PipRange getWireAliases(WireId wire) const + { + // TODO: consider always-on intra-tile pips to be aliases? + PipRange ret; + ret.b.arch = this; + ret.b.wire = wire; + ret.b.mode = PipIterator::MODE_DOWNHILL; + ret.b.stage = PipIterator::STAGE_PORTS; + ret.b.cursor_index = -1; + ++ret.b; + ret.e.arch = this; + ret.e.wire = wire; + ret.e.mode = PipIterator::MODE_DOWNHILL; + ret.e.stage = PipIterator::STAGE_END; + ret.e.cursor_index = 0; + return ret; + } + + Loc getPipLocation(PipId pip) const + { + Loc loc; + loc.x = pip.location.x; + loc.y = pip.location.y; + loc.z = 0; + return loc; + } + + BelId getPackagePinBel(const std::string &pin) const; + std::string getBelPackagePin(BelId bel) const; + int getPioBelBank(BelId bel) const; + // For getting GCLK, PLL, Vref, etc, pins + std::string getPioFunctionName(BelId bel) const; + BelId getPioByFunctionName(const std::string &name) const; + + PortType getBelPinType(BelId bel, IdString pin) const; + + // ------------------------------------------------- + + GroupId getGroupByName(IdString name) const { return GroupId(); } + IdString getGroupName(GroupId group) const { return IdString(); } + std::vector getGroups() const { return std::vector(); } + std::vector getGroupBels(GroupId group) const { return std::vector(); } + std::vector getGroupWires(GroupId group) const { return std::vector(); } + std::vector getGroupPips(GroupId group) const { return std::vector(); } + std::vector getGroupGroups(GroupId group) const { return std::vector(); } + + // ------------------------------------------------- + + delay_t estimateDelay(WireId src, WireId dst) const; + delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const; + delay_t getDelayEpsilon() const { return 20; } + delay_t getRipupDelayPenalty() const { return 200; } + float getDelayNS(delay_t v) const { return v * 0.001; } + DelayInfo getDelayFromNS(float ns) const + { + DelayInfo del; + del.min_delay = delay_t(ns * 1000); + del.max_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 + 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; + + // ------------------------------------------------- + // Placement validity checks + // TODO: validate bel subtype (SLICEM vs SLICEL, IOBM vs IOBS, ...). + bool isValidBelForCell(CellInfo *cell, BelId bel) const { return true; } + bool isBelLocationValid(BelId bel) const { return true; } + + //void assignArchInfo(); +}; + +NEXTPNR_NAMESPACE_END diff --git a/leuctra/arch_pybindings.cc b/leuctra/arch_pybindings.cc new file mode 100644 index 00000000..186b2c13 --- /dev/null +++ b/leuctra/arch_pybindings.cc @@ -0,0 +1,42 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 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() +{ + using namespace PythonConversion; + auto arch_cls = class_, boost::noncopyable>("Arch", init()); + auto ctx_cls = class_, boost::noncopyable>("Context", no_init) + .def("checksum", &Context::checksum) + .def("pack", &Context::pack) + .def("place", &Context::place) + .def("route", &Context::route); +} + +NEXTPNR_NAMESPACE_END + +#endif \ No newline at end of file diff --git a/leuctra/arch_pybindings.h b/leuctra/arch_pybindings.h new file mode 100644 index 00000000..9bd7bcdf --- /dev/null +++ b/leuctra/arch_pybindings.h @@ -0,0 +1,83 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 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); + } +}; + +} // namespace PythonConversion + +NEXTPNR_NAMESPACE_END +#endif +#endif diff --git a/leuctra/archdefs.h b/leuctra/archdefs.h new file mode 100644 index 00000000..39145492 --- /dev/null +++ b/leuctra/archdefs.h @@ -0,0 +1,234 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * 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 + +#include + +NEXTPNR_NAMESPACE_BEGIN + +typedef int delay_t; + +struct DelayInfo +{ + delay_t min_delay = 0, max_delay = 0; + + delay_t minRaiseDelay() const { return min_delay; } + delay_t maxRaiseDelay() const { return max_delay; } + + delay_t minFallDelay() const { return min_delay; } + delay_t maxFallDelay() const { return max_delay; } + + delay_t minDelay() const { return min_delay; } + delay_t maxDelay() const { return max_delay; } + + DelayInfo operator+(const DelayInfo &other) const + { + DelayInfo ret; + ret.min_delay = this->min_delay + other.min_delay; + ret.max_delay = this->max_delay + other.max_delay; + return ret; + } +}; + +// ----------------------------------------------------------------------- + +NPNR_PACKED_STRUCT(struct LocationPOD { int16_t x, y; }); + +struct Location +{ + int16_t x = -1, y = -1; + Location() : x(-1), y(-1){}; + Location(int16_t x, int16_t y) : x(x), y(y){}; + Location(const LocationPOD &pod) : x(pod.x), y(pod.y){}; + Location(const Location &loc) : x(loc.x), y(loc.y){}; + + bool operator==(const Location &other) const { return x == other.x && y == other.y; } + bool operator!=(const Location &other) const { return x != other.x || y != other.y; } + bool operator<(const Location &other) const { return y == other.y ? x < other.x : y < other.y; } +}; + +inline Location operator+(const Location &a, const Location &b) { return Location(a.x + b.x, a.y + b.y); } + +struct BelId +{ + Location location; + int32_t index = -1; + + bool operator==(const BelId &other) const { return index == other.index && location == other.location; } + bool operator!=(const BelId &other) const { return index != other.index || location != other.location; } + bool operator<(const BelId &other) const + { + return location == other.location ? index < other.index : location < other.location; + } +}; + +struct WireId +{ + Location location; + int32_t index = -1; + + bool operator==(const WireId &other) const { return index == other.index && location == other.location; } + bool operator!=(const WireId &other) const { return index != other.index || location != other.location; } + bool operator<(const WireId &other) const + { + return location == other.location ? index < other.index : location < other.location; + } +}; + +enum PipKind { + PIP_KIND_PIP, + PIP_KIND_PORT, +}; + +struct PipId +{ + Location location; + // Is it a plain pip, or a pseudo-pip for a port. + PipKind kind; + // Index of the pip or the port. + int32_t index = -1; + // For ports, index of the wire in the port. + int32_t subindex = -1; + + bool operator==(const PipId &other) const { return subindex == other.subindex && index == other.index && kind == other.kind && location == other.location; } + bool operator!=(const PipId &other) const { return subindex != other.subindex || index != other.index || kind != other.kind || location != other.location; } + bool operator<(const PipId &other) const + { + if (location != other.location) + return location < other.location; + if (kind != other.kind) + return kind < other.kind; + if (index != other.index) + return index < other.index; + return subindex < other.subindex; + } +}; + +struct GroupId +{ + int32_t index = -1; + + bool operator==(const GroupId &other) const { return index == other.index; } + bool operator!=(const GroupId &other) const { return index != other.index; } +}; + +struct DecalId +{ + enum + { + TYPE_NONE, + TYPE_BEL + } type; + Location location; + uint32_t z = 0; + bool active = false; + bool operator==(const DecalId &other) const + { + return type == other.type && location == other.location && z == other.z && active == other.active; + } + bool operator!=(const DecalId &other) const + { + return type != other.type || location != other.location || z != other.z || active != other.active; + } +}; + +struct ArchNetInfo +{ + bool is_global = false; +}; + +struct ArchCellInfo +{ + struct + { + bool using_dff; + bool has_l6mux; + IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode; + } sliceInfo; +}; + +NEXTPNR_NAMESPACE_END + +namespace std { +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Location &loc) const noexcept + { + std::size_t seed = std::hash()(loc.x); + seed ^= std::hash()(loc.y) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept + { + std::size_t seed = std::hash()(bel.location); + seed ^= std::hash()(bel.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept + { + std::size_t seed = std::hash()(wire.location); + seed ^= std::hash()(wire.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept + { + std::size_t seed = std::hash()(pip.location); + seed ^= std::hash()(pip.index) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept + { + return std::hash()(group.index); + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(decal.type)); + boost::hash_combine(seed, hash()(decal.location)); + boost::hash_combine(seed, hash()(decal.z)); + boost::hash_combine(seed, hash()(decal.active)); + return seed; + } +}; + +} // namespace std diff --git a/leuctra/family.cmake b/leuctra/family.cmake new file mode 100644 index 00000000..e69de29b diff --git a/leuctra/main.cc b/leuctra/main.cc new file mode 100644 index 00000000..79ebc002 --- /dev/null +++ b/leuctra/main.cc @@ -0,0 +1,75 @@ +/* + * 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 "log.h" +#include "timing.h" + +USING_NEXTPNR_NAMESPACE + +class LeuctraCommandHandler : public CommandHandler +{ + public: + LeuctraCommandHandler(int argc, char **argv); + virtual ~LeuctraCommandHandler(){}; + std::unique_ptr createContext() override; + void setupArchContext(Context *ctx) override{}; + void customBitstream(Context *ctx) override; + + protected: + po::options_description getArchOptions() override; +}; + +LeuctraCommandHandler::LeuctraCommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {} + +po::options_description LeuctraCommandHandler::getArchOptions() +{ + po::options_description specific("Architecture specific options"); + specific.add_options()("device", po::value(), "select device"); + specific.add_options()("package", po::value(), "select device package"); + specific.add_options()("speed", po::value(), "select device speedgrade"); + return specific; +} + +void LeuctraCommandHandler::customBitstream(Context *ctx) { log_error("Here is when bitstream gets created"); } + +std::unique_ptr LeuctraCommandHandler::createContext() +{ + if (vm.count("device")) + chipArgs.device = vm["device"].as(); + else + chipArgs.device = "xc6slx9"; + if (vm.count("package")) + chipArgs.package = vm["package"].as(); + if (vm.count("speed")) + chipArgs.speed = vm["speed"].as(); + return std::unique_ptr(new Context(chipArgs)); +} + +int main(int argc, char *argv[]) +{ + LeuctraCommandHandler handler(argc, argv); + return handler.exec(); +} + +#endif diff --git a/leuctra/pack.cc b/leuctra/pack.cc new file mode 100644 index 00000000..575fe513 --- /dev/null +++ b/leuctra/pack.cc @@ -0,0 +1,31 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 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 "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +// Main pack function +bool Arch::pack() +{ + // XXX + return true; +} + +NEXTPNR_NAMESPACE_END diff --git a/leuctra/project.cc b/leuctra/project.cc new file mode 100644 index 00000000..d58abc7a --- /dev/null +++ b/leuctra/project.cc @@ -0,0 +1,47 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 "project.h" +#include +#include +#include +#include "log.h" + +NEXTPNR_NAMESPACE_BEGIN + +void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path) +{ + root.put("project.arch.package", ctx->archArgs().package); + root.put("project.arch.speed", ctx->archArgs().speed); +} + +std::unique_ptr ProjectHandler::createContext(pt::ptree &root) +{ + ArchArgs chipArgs; + chipArgs.device = root.get("project.arch.type"); + chipArgs.package = root.get("project.arch.package"); + chipArgs.speed = root.get("project.arch.speed"); + + return std::unique_ptr(new Context(chipArgs)); +} + +void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path) {} + +NEXTPNR_NAMESPACE_END +