/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2018  Clifford Wolf <clifford@symbioticeda.com>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#ifndef NEXTPNR_H
#error Include "arch.h" via "nextpnr.h" only.
#endif

#include "torc/Architecture.hpp"
#include "torc/Common.hpp"
using namespace torc::architecture;
using namespace torc::architecture::xilinx;

namespace std {
  template <> struct hash<Segments::SegmentReference>
  {
    size_t operator()(const Segments::SegmentReference &s) const
    {
        size_t seed = 0;
        boost::hash_combine(seed, hash<unsigned>()(s.getCompactSegmentIndex()));
        boost::hash_combine(seed, hash<unsigned>()(s.getAnchorTileIndex()));
        return seed;
    }
  };
  template <> struct equal_to<Segments::SegmentReference>
  {
    bool operator()(const Segments::SegmentReference &lhs, const Segments::SegmentReference &rhs) const
    {
        return lhs.getAnchorTileIndex() == rhs.getAnchorTileIndex() && lhs.getCompactSegmentIndex() == rhs.getCompactSegmentIndex();
    }
  };
  template <> struct hash<Tilewire>
  {
    size_t operator()(const Tilewire& t) const
    {
        return hash_value(t);
    }
  };

  template <> struct hash<Arc>
  {
    size_t operator()(const Arc &a) const
    {
        size_t seed = 0;
        boost::hash_combine(seed, hash_value(a.getSourceTilewire()));
        boost::hash_combine(seed, hash_value(a.getSinkTilewire()));
        return seed;
    }
  };
}

NEXTPNR_NAMESPACE_BEGIN

/**** Everything in this section must be kept in sync with chipdb.py ****/

template <typename T> struct RelPtr
{
    int32_t offset;

    // void set(const T *ptr) {
    //     offset = reinterpret_cast<const char*>(ptr) -
    //              reinterpret_cast<const char*>(this);
    // }

    const T *get() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + offset); }

    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 BelWirePOD {
    int32_t port;
    int32_t type;
    int32_t wire_index;
});

NPNR_PACKED_STRUCT(struct BelInfoPOD {
    RelPtr<char> name;
    int32_t type;
    int32_t num_bel_wires;
    RelPtr<BelWirePOD> bel_wires;
    int8_t x, y, z;
    int8_t padding_0;
});

NPNR_PACKED_STRUCT(struct BelPortPOD {
    int32_t bel_index;
    int32_t port;
});

NPNR_PACKED_STRUCT(struct PipInfoPOD {
    enum PipFlags : uint32_t
    {
        FLAG_NONE = 0,
        FLAG_ROUTETHRU = 1,
        FLAG_NOCARRY = 2
    };

    // RelPtr<char> name;
    int32_t src, dst;
    int32_t fast_delay;
    int32_t slow_delay;
    int8_t x, y;
    int16_t src_seg, dst_seg;
    int16_t switch_mask;
    int32_t switch_index;
    PipFlags flags;
});

NPNR_PACKED_STRUCT(struct WireSegmentPOD {
    int8_t x, y;
    int16_t index;
});

NPNR_PACKED_STRUCT(struct WireInfoPOD {
    enum WireType : int8_t
    {
        WIRE_TYPE_NONE = 0,
        WIRE_TYPE_GLB2LOCAL = 1,
        WIRE_TYPE_GLB_NETWK = 2,
        WIRE_TYPE_LOCAL = 3,
        WIRE_TYPE_LUTFF_IN = 4,
        WIRE_TYPE_LUTFF_IN_LUT = 5,
        WIRE_TYPE_LUTFF_LOUT = 6,
        WIRE_TYPE_LUTFF_OUT = 7,
        WIRE_TYPE_LUTFF_COUT = 8,
        WIRE_TYPE_LUTFF_GLOBAL = 9,
        WIRE_TYPE_CARRY_IN_MUX = 10,
        WIRE_TYPE_SP4_V = 11,
        WIRE_TYPE_SP4_H = 12,
        WIRE_TYPE_SP12_V = 13,
        WIRE_TYPE_SP12_H = 14
    };

    RelPtr<char> name;
    int32_t num_uphill, num_downhill;
    RelPtr<int32_t> pips_uphill, pips_downhill;

    int32_t num_bel_pins;
    RelPtr<BelPortPOD> bel_pins;

    int32_t num_segments;
    RelPtr<WireSegmentPOD> segments;

    int32_t fast_delay;
    int32_t slow_delay;

    int8_t x, y, z;
    WireType type;
});

NPNR_PACKED_STRUCT(struct PackagePinPOD {
    RelPtr<char> name;
    int32_t bel_index;
});

NPNR_PACKED_STRUCT(struct PackageInfoPOD {
    RelPtr<char> name;
    int32_t num_pins;
    RelPtr<PackagePinPOD> pins;
});

enum TileType : uint32_t
{
    TILE_NONE = 0,
    TILE_LOGIC = 1,
    TILE_IO = 2,
    TILE_RAMB = 3,
    TILE_RAMT = 4,
    TILE_DSP0 = 5,
    TILE_DSP1 = 6,
    TILE_DSP2 = 7,
    TILE_DSP3 = 8,
    TILE_IPCON = 9
};

NPNR_PACKED_STRUCT(struct ConfigBitPOD { int8_t row, col; });

NPNR_PACKED_STRUCT(struct ConfigEntryPOD {
    RelPtr<char> name;
    int32_t num_bits;
    RelPtr<ConfigBitPOD> bits;
});

NPNR_PACKED_STRUCT(struct TileInfoPOD {
    int8_t cols, rows;
    int16_t num_config_entries;
    RelPtr<ConfigEntryPOD> entries;
});

static const int max_switch_bits = 5;

NPNR_PACKED_STRUCT(struct SwitchInfoPOD {
    int32_t num_bits;
    int32_t bel;
    int8_t x, y;
    ConfigBitPOD cbits[max_switch_bits];
});

NPNR_PACKED_STRUCT(struct IerenInfoPOD {
    int8_t iox, ioy, ioz;
    int8_t ierx, iery, ierz;
});

NPNR_PACKED_STRUCT(struct BitstreamInfoPOD {
    int32_t num_switches, num_ierens;
    RelPtr<TileInfoPOD> tiles_nonrouting;
    RelPtr<SwitchInfoPOD> switches;
    RelPtr<IerenInfoPOD> ierens;
});

NPNR_PACKED_STRUCT(struct BelConfigEntryPOD {
    RelPtr<char> entry_name;
    RelPtr<char> cbit_name;
    int8_t x, y;
    int16_t padding;
});

// Stores mapping between bel parameters and config bits,
// for extra cells where this mapping is non-trivial
NPNR_PACKED_STRUCT(struct BelConfigPOD {
    int32_t bel_index;
    int32_t num_entries;
    RelPtr<BelConfigEntryPOD> entries;
});

NPNR_PACKED_STRUCT(struct CellPathDelayPOD {
    int32_t from_port;
    int32_t to_port;
    int32_t fast_delay;
    int32_t slow_delay;
});

NPNR_PACKED_STRUCT(struct CellTimingPOD {
    int32_t type;
    int32_t num_paths;
    RelPtr<CellPathDelayPOD> path_delays;
});

NPNR_PACKED_STRUCT(struct ChipInfoPOD {
    int32_t width, height;
    int32_t num_bels, num_wires, num_pips;
    int32_t num_switches, num_belcfgs, num_packages;
    int32_t num_timing_cells;
    RelPtr<BelInfoPOD> bel_data;
    RelPtr<WireInfoPOD> wire_data;
    RelPtr<PipInfoPOD> pip_data;
    RelPtr<TileType> tile_grid;
    RelPtr<BitstreamInfoPOD> bits_info;
    RelPtr<BelConfigPOD> bel_config;
    RelPtr<PackageInfoPOD> packages_data;
    RelPtr<CellTimingPOD> cell_timing;
});


struct Arch;
struct TorcInfo {
    TorcInfo(Arch *ctx, const std::string &inDeviceName, const std::string &inPackageName);
    std::unique_ptr<const DDB> ddb;
    const Sites &sites;
    const Tiles &tiles;
    const Segments &segments;

    const TileInfo& bel_to_tile_info(int32_t index) const {
        auto si = bel_to_site_index[index];
        auto &site = sites.getSite(si);
        return tiles.getTileInfo(site.getTileIndex());
    }
    const std::string& bel_to_name(int32_t index) const {
        auto si = bel_to_site_index[index];
        return sites.getSite(si).getName();
    }
    std::string wire_to_name(int32_t index) const
    {
        const auto &tw = wire_to_tilewire[index];
        ExtendedWireInfo ewi(*ddb, tw);
        std::stringstream ss;
        ss << ewi.mTileName << "/" << ewi.mWireName;
        ss << "(" << tw.getWireIndex() << "@" << tw.getTileIndex() << ")";
        return ss.str();
    }
    int32_t tilewire_to_wire(const Tilewire &tw) const
    {
        const auto &segment = segments.getTilewireSegment(tw);
        if (!segment.isTrivial())
            return segment_to_wire.at(segment);
        return trivial_to_wire.at(tw);
    }

    const std::vector<SiteIndex> bel_to_site_index;
    const int num_bels;
    const std::vector<IdString> site_index_to_type;
    const std::vector<int8_t> bel_to_z;
    std::unordered_map<Segments::SegmentReference,int> segment_to_wire;
    std::unordered_map<Tilewire,int> trivial_to_wire;
    const std::vector<Tilewire> wire_to_tilewire;
    const int num_wires;
    std::vector<std::vector<int>> wire_to_pips_uphill;
    std::vector<std::vector<int>> wire_to_pips_downhill;
    const std::vector<Arc> pip_to_arc;
    const int num_pips;

private:
    static std::vector<SiteIndex> construct_bel_to_site_index(Arch *ctx, const Sites &sites);
    static std::vector<IdString> construct_site_index_to_type(Arch *ctx, const Sites &sites);
    static std::vector<int8_t> construct_bel_to_z(const Sites &sites, const int num_bels, const std::vector<IdString> &site_index_to_type);
    static std::vector<Tilewire> construct_wire_to_tilewire(const Segments &segments, const Tiles &tiles, std::unordered_map<Segments::SegmentReference,int>& segment_to_wire, std::unordered_map<Tilewire,int>& trivial_to_wire);
    static std::vector<Arc> construct_pip_to_arc(const std::vector<Tilewire>& wire_to_tilewire, const DDB& ddb, std::vector<std::vector<int>> &wire_to_pips_uphill, std::vector<std::vector<int>> &wire_to_pips_downhill);
};
extern std::unique_ptr<const TorcInfo> torc_info;


/************************ End of chipdb section. ************************/

struct BelIterator
{
    int cursor;

    BelIterator operator++()
    {
        cursor++;
        return *this;
    }
    BelIterator operator++(int)
    {
        BelIterator prior(*this);
        cursor++;
        return prior;
    }

    bool operator!=(const BelIterator &other) const { return cursor != other.cursor; }

    bool operator==(const BelIterator &other) const { return cursor == other.cursor; }

    BelId operator*() const
    {
        BelId ret;
        ret.index = cursor;
        return ret;
    }
};

struct BelRange
{
    BelIterator b, e;
    BelIterator begin() const { return b; }
    BelIterator end() const { return e; }
};

// -----------------------------------------------------------------------

struct BelPinIterator
{
    const BelId bel;
    Array<const WireIndex>::iterator it;

    void operator++() { it++; }
    bool operator!=(const BelPinIterator &other) const { return it != other.it && bel != other.bel; }

    BelPin operator*() const
    {
        BelPin ret;
        ret.bel = bel;
        ret.pin = IdString();
        return ret;
    }
};

struct BelPinRange
{
    BelPinIterator b, e;
    BelPinIterator begin() const { return b; }
    BelPinIterator end() const { return e; }
};

// -----------------------------------------------------------------------

struct WireIterator
{
    int cursor = -1;

    void operator++() { cursor++; }
    bool operator!=(const WireIterator &other) const { return cursor != other.cursor; }

    WireId operator*() const
    {
        WireId ret;
        ret.index = cursor;
        return ret;
    }
};

struct WireRange
{
    WireIterator b, e;
    WireIterator begin() const { return b; }
    WireIterator end() const { return e; }
};

// -----------------------------------------------------------------------

struct AllPipIterator
{
    int cursor = -1;

    void operator++() { cursor++; }
    bool operator!=(const AllPipIterator &other) const { return cursor != other.cursor; }

    PipId operator*() const
    {
        PipId ret;
        ret.index = cursor;
        return ret;
    }
};

struct AllPipRange
{
    AllPipIterator b, e;
    AllPipIterator begin() const { return b; }
    AllPipIterator end() const { return e; }
};

// -----------------------------------------------------------------------

struct PipIterator
{
    const int *cursor = nullptr;

    void operator++() { cursor++; }
    bool operator!=(const PipIterator &other) const { return cursor != other.cursor; }

    PipId operator*() const
    {
        PipId ret;
        ret.index = *cursor;
        return ret;
    }
};

struct PipRange
{
    PipIterator b, e;
    PipIterator begin() const { return b; }
    PipIterator end() const { return e; }
};

struct ArchArgs
{
    enum ArchArgsTypes
    {
        NONE,
        Z020
    } type = NONE;
    std::string package;
};

struct Arch : BaseCtx
{
    bool fast_part;
    const ChipInfoPOD *chip_info;
    const PackageInfoPOD *package_info;

    mutable std::unordered_map<IdString, int> wire_by_name;
    mutable std::unordered_map<IdString, int> pip_by_name;
    mutable std::unordered_map<Loc, BelId> bel_by_loc;

    //std::vector<bool> bel_carry;
    std::vector<CellInfo *> bel_to_cell;
    std::vector<NetInfo *> wire_to_net;
    std::vector<NetInfo *> pip_to_net;
    //std::vector<NetInfo *> switches_locked;

    ArchArgs args;
    Arch(ArchArgs args);

    std::string getChipName() const;

    IdString archId() const { return id("xc7"); }
    ArchArgs archArgs() const { return args; }
    IdString archArgsToId(ArchArgs args) const;

    // -------------------------------------------------

    int getGridDimX() const { return 34; }
    int getGridDimY() const { return 34; }
    int getTileBelDimZ(int, int) const { return 8; }
    int getTilePipDimZ(int, int) const { return 1; }

    // -------------------------------------------------

    BelId getBelByName(IdString name) const;

    IdString getBelName(BelId bel) const
    {
        NPNR_ASSERT(bel != BelId());
        auto name = torc_info->bel_to_name(bel.index);
        if (getBelType(bel) == id_SLICE_LUT6) {
            // Append LUT name to name
            name.reserve(name.size() + 2);
            name += "_";
            switch (torc_info->bel_to_z[bel.index]) {
                case 0: case 4: name += 'A'; break;
                case 1: case 5: name += 'B'; break;
                case 2: case 6: name += 'C'; break;
                case 3: case 7: name += 'D'; break;
                default: throw;
            }
        }
        return id(name);
    }

    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.index] == nullptr);

        bel_to_cell[bel.index] = cell;
        //bel_carry[bel.index] = (cell->type == id_ICESTORM_LC && cell->lcInfo.carryEnable);
        cell->bel = bel;
        cell->belStrength = strength;
        refreshUiBel(bel);
    }

    void unbindBel(BelId bel)
    {
        NPNR_ASSERT(bel != BelId());
        NPNR_ASSERT(bel_to_cell[bel.index] != nullptr);
        bel_to_cell[bel.index]->bel = BelId();
        bel_to_cell[bel.index]->belStrength = STRENGTH_NONE;
        bel_to_cell[bel.index] = nullptr;
        //bel_carry[bel.index] = false;
        refreshUiBel(bel);
    }

    bool checkBelAvail(BelId bel) const
    {
        NPNR_ASSERT(bel != BelId());
        return bel_to_cell[bel.index] == nullptr;
    }

    CellInfo *getBoundBelCell(BelId bel) const
    {
        NPNR_ASSERT(bel != BelId());
        return bel_to_cell[bel.index];
    }

    CellInfo *getConflictingBelCell(BelId bel) const
    {
        NPNR_ASSERT(bel != BelId());
        return bel_to_cell[bel.index];
    }

    BelRange getBels() const
    {
        BelRange range;
        range.b.cursor = 0;
        range.e.cursor = torc_info->num_bels;
        return range;
    }

    Loc getBelLocation(BelId bel) const
    {
        const auto &tile_info = torc_info->bel_to_tile_info(bel.index);

        Loc loc;
        loc.x = tile_info.getCol(); 
        loc.y = tile_info.getRow();
        loc.z = torc_info->bel_to_z[bel.index];
        return loc;
    }

    BelId getBelByLocation(Loc loc) const;
    BelRange getBelsByTile(int x, int y) const;

    bool getBelGlobalBuf(BelId bel) const { return getBelType(bel) == id_BUFGCTRL; }

    IdString getBelType(BelId bel) const
    {
        NPNR_ASSERT(bel != BelId());
        auto site_index = torc_info->bel_to_site_index[bel.index];
        return torc_info->site_index_to_type[site_index];
    }

    WireId getBelPinWire(BelId bel, IdString pin) const;
    PortType getBelPinType(BelId bel, IdString pin) const;
    std::vector<IdString> getBelPins(BelId bel) const;

    // -------------------------------------------------

    WireId getWireByName(IdString name) const;

    IdString getWireName(WireId wire) const
    {
        NPNR_ASSERT(wire != WireId());
        return id(torc_info->wire_to_name(wire.index));
    }

    IdString getWireType(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.index] == nullptr);
        wire_to_net[wire.index] = 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.index] != nullptr);

        auto &net_wires = wire_to_net[wire.index]->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.index] = nullptr;
            //switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
        }

        net_wires.erase(it);
        wire_to_net[wire.index] = nullptr;
        refreshUiWire(wire);
    }

    bool checkWireAvail(WireId wire) const
    {
        NPNR_ASSERT(wire != WireId());
        return wire_to_net[wire.index] == nullptr;
    }

    NetInfo *getBoundWireNet(WireId wire) const
    {
        NPNR_ASSERT(wire != WireId());
        return wire_to_net[wire.index];
    }

    NetInfo *getConflictingWireNet(WireId wire) const
    {
        NPNR_ASSERT(wire != WireId());
        return wire_to_net[wire.index];
    }

    DelayInfo getWireDelay(WireId wire) const
    {
        DelayInfo delay;
        //NPNR_ASSERT(wire != WireId());
        //if (fast_part)
        //    delay.delay = chip_info->wire_data[wire.index].fast_delay;
        //else
        //    delay.delay = chip_info->wire_data[wire.index].slow_delay;
        return delay;
    }

    BelPinRange getWireBelPins(WireId wire) const
    {
        BelPinRange range;
        //NPNR_ASSERT(wire != WireId());
        //range.b.ptr = chip_info->wire_data[wire.index].bel_pins.get();
        //range.e.ptr = range.b.ptr + chip_info->wire_data[wire.index].num_bel_pins;
        throw;
        return range;
    }

    WireRange getWires() const
    {
        WireRange range;
        range.b.cursor = 0;
        range.e.cursor = torc_info->num_wires;
        return range;
    }

    // -------------------------------------------------

    PipId getPipByName(IdString name) const;

    void bindPip(PipId pip, NetInfo *net, PlaceStrength strength)
    {
        NPNR_ASSERT(pip != PipId());
        NPNR_ASSERT(pip_to_net[pip.index] == nullptr);
        //NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] == nullptr);

        pip_to_net[pip.index] = net;
        //switches_locked[chip_info->pip_data[pip.index].switch_index] = net;

        WireId dst = getPipDstWire(pip);
        NPNR_ASSERT(wire_to_net[dst.index] == nullptr);
        wire_to_net[dst.index] = 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.index] != nullptr);
        //NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] != nullptr);

        WireId dst = getPipDstWire(pip);
        NPNR_ASSERT(wire_to_net[dst.index] != nullptr);
        wire_to_net[dst.index] = nullptr;
        pip_to_net[pip.index]->wires.erase(dst);

        pip_to_net[pip.index] = nullptr;
        //switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
        refreshUiPip(pip);
        refreshUiWire(dst);
    }

    bool checkPipAvail(PipId pip) const
    {
        NPNR_ASSERT(pip != PipId());
        //auto &pi = chip_info->pip_data[pip.index];
        //auto &si = chip_info->bits_info->switches[pi.switch_index];

        //if (switches_locked[pi.switch_index] != nullptr)
        //    return false;

        //if (pi.flags & PipInfoPOD::FLAG_ROUTETHRU) {
        //    NPNR_ASSERT(si.bel >= 0);
        //    if (bel_to_cell[si.bel] != nullptr)
        //        return false;
        //}

        //if (pi.flags & PipInfoPOD::FLAG_NOCARRY) {
        //    NPNR_ASSERT(si.bel >= 0);
        //    if (bel_carry[si.bel])
        //        return false;
        //}

        //return true;
        return pip_to_net[pip.index] == nullptr;
    }

    NetInfo *getBoundPipNet(PipId pip) const
    {
        NPNR_ASSERT(pip != PipId());
        return pip_to_net[pip.index];
    }

    NetInfo *getConflictingPipNet(PipId pip) const
    {
        NPNR_ASSERT(pip != PipId());
        //return switches_locked[chip_info->pip_data[pip.index].switch_index];
        return pip_to_net[pip.index];
    }

    AllPipRange getPips() const
    {
        AllPipRange range;
        range.b.cursor = 0;
        range.e.cursor = torc_info->num_pips;
        return range;
    }

    Loc getPipLocation(PipId pip) const
    {
        const auto &arc = torc_info->pip_to_arc[pip.index];
        const auto &tw = arc.getSourceTilewire();
        const auto &tile_info = torc_info->tiles.getTileInfo(tw.getTileIndex());

        Loc loc;
        loc.x = tile_info.getCol(); 
        loc.y = tile_info.getRow();
        loc.z = 0;
        return loc;
    }

    IdString getPipName(PipId pip) const;

    IdString getPipType(PipId pip) const { return IdString(); }

    uint32_t getPipChecksum(PipId pip) const { return pip.index; }

    WireId getPipSrcWire(PipId pip) const
    {
        WireId wire;
        NPNR_ASSERT(pip != PipId());

        const auto &arc = torc_info->pip_to_arc[pip.index];
        const auto &tw = arc.getSourceTilewire();
        wire.index = torc_info->tilewire_to_wire(tw);

        return wire;
    }

    WireId getPipDstWire(PipId pip) const
    {
        WireId wire;
        NPNR_ASSERT(pip != PipId());

        const auto &arc = torc_info->pip_to_arc[pip.index];
        const auto &tw = arc.getSinkTilewire();
        wire.index = torc_info->tilewire_to_wire(tw);

        return wire;
    }

    DelayInfo getPipDelay(PipId pip) const
    {
        DelayInfo delay;
        NPNR_ASSERT(pip != PipId());
        //if (fast_part)
        //    delay.delay = chip_info->pip_data[pip.index].fast_delay;
        //else
        //    delay.delay = chip_info->pip_data[pip.index].slow_delay;
        return delay;
    }

    PipRange getPipsDownhill(WireId wire) const
    {
        PipRange range;
        NPNR_ASSERT(wire != WireId());
        const auto &pips = torc_info->wire_to_pips_downhill[wire.index];
        range.b.cursor = pips.data();
        range.e.cursor = range.b.cursor + pips.size();

        return range;
    }

    PipRange getPipsUphill(WireId wire) const
    {
        PipRange range;
        NPNR_ASSERT(wire != WireId());
        const auto &pips = torc_info->wire_to_pips_uphill[wire.index];
        range.b.cursor = pips.data();
        range.e.cursor = range.b.cursor + pips.size();
        return range;
    }

    PipRange getWireAliases(WireId wire) const
    {
        PipRange range;
        NPNR_ASSERT(wire != WireId());
        range.b.cursor = nullptr;
        range.e.cursor = nullptr;
        return range;
    }

    BelId getPackagePinBel(const std::string &pin) const;
    std::string getBelPackagePin(BelId bel) const;

    // -------------------------------------------------

    GroupId getGroupByName(IdString name) const;
    IdString getGroupName(GroupId group) const;
    std::vector<GroupId> getGroups() const;
    std::vector<BelId> getGroupBels(GroupId group) const;
    std::vector<WireId> getGroupWires(GroupId group) const;
    std::vector<PipId> getGroupPips(GroupId group) const;
    std::vector<GroupId> getGroupGroups(GroupId group) const;

    // -------------------------------------------------

    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; }
    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<GraphicElement> 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 clockDomain if applicable
    TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockDomain) const;
    // Return true if a port is a net
    bool isGlobalNet(const NetInfo *net) const;

    // -------------------------------------------------

    // Perform placement validity checks, returning false on failure (all
    // implemented in arch_place.cc)

    // 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;

    // Return true whether all Bels at a given location are valid
    bool isBelLocationValid(BelId bel) const;

    // Helper function for above
    bool logicCellsCompatible(const CellInfo** it, const size_t size) const;

    // -------------------------------------------------
    // Assign architecure-specific arguments to nets and cells, which must be
    // called between packing or further
    // netlist modifications, and validity checks
    void assignArchInfo();
    void assignCellInfo(CellInfo *cell);

    // -------------------------------------------------
    BelPin getIOBSharingPLLPin(BelId pll, IdString pll_pin) const
    {
        auto wire = getBelPinWire(pll, pll_pin);
        for (auto src_bel : getWireBelPins(wire)) {
            if (getBelType(src_bel.bel) == id_SB_IO && src_bel.pin == id_D_IN_0) {
                return src_bel;
            }
        }
        NPNR_ASSERT_FALSE("Expected PLL pin to share an output with an SB_IO D_IN_{0,1}");
    }

    float placer_constraintWeight = 10;
};

//void ice40DelayFuzzerMain(Context *ctx);

NEXTPNR_NAMESPACE_END
