2020-06-13 05:09:46 +08:00
|
|
|
/*
|
|
|
|
* nextpnr -- Next Generation Place and Route
|
|
|
|
*
|
2021-01-07 00:22:17 +08:00
|
|
|
* Copyright (C) 2021 Lofty <dan.ravensloft@gmail.com>
|
2020-06-13 05:09:46 +08:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2021-05-01 01:40:24 +08:00
|
|
|
#ifndef MISTRAL_ARCH_H
|
|
|
|
#define MISTRAL_ARCH_H
|
2020-06-13 05:09:46 +08:00
|
|
|
|
2021-05-01 01:40:24 +08:00
|
|
|
#include <set>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#include "base_arch.h"
|
|
|
|
#include "nextpnr_types.h"
|
|
|
|
#include "relptr.h"
|
|
|
|
|
|
|
|
#include "cyclonev.h"
|
2020-06-13 05:09:46 +08:00
|
|
|
|
2021-01-07 00:22:17 +08:00
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
2020-06-13 05:09:46 +08:00
|
|
|
|
|
|
|
struct ArchArgs
|
|
|
|
{
|
2021-01-07 00:22:17 +08:00
|
|
|
std::string device;
|
2021-05-01 20:40:45 +08:00
|
|
|
std::string mistral_root;
|
2020-06-13 05:09:46 +08:00
|
|
|
};
|
|
|
|
|
2021-05-03 03:40:27 +08:00
|
|
|
// These structures are used for fast ALM validity checking
|
|
|
|
struct ALMInfo
|
|
|
|
{
|
|
|
|
// Pointers to bels
|
|
|
|
std::array<BelId, 2> lut_bels;
|
|
|
|
std::array<BelId, 4> ff_bels;
|
|
|
|
// TODO: ALM configuration (L5/L6 mode, LUT input permutation, etc)
|
|
|
|
};
|
|
|
|
|
|
|
|
struct LABInfo
|
|
|
|
{
|
|
|
|
std::array<ALMInfo, 10> alms;
|
2021-05-03 04:41:09 +08:00
|
|
|
// Control set wires
|
|
|
|
std::array<WireId, 3> clk_wires, ena_wires;
|
|
|
|
std::array<WireId, 2> aclr_wires;
|
|
|
|
WireId sclr_wire, sload_wire;
|
2021-05-03 03:40:27 +08:00
|
|
|
// TODO: LAB configuration (control set etc)
|
|
|
|
};
|
|
|
|
|
2021-02-04 10:29:59 +08:00
|
|
|
struct PinInfo
|
|
|
|
{
|
|
|
|
WireId wire;
|
2021-05-03 03:40:27 +08:00
|
|
|
PortType dir;
|
2021-02-04 10:29:59 +08:00
|
|
|
};
|
|
|
|
|
2021-02-03 08:12:14 +08:00
|
|
|
struct BelInfo
|
|
|
|
{
|
2021-05-03 03:40:27 +08:00
|
|
|
IdString name;
|
|
|
|
IdString type;
|
|
|
|
IdString bucket;
|
2021-05-03 22:12:24 +08:00
|
|
|
// For cases where we need to determine an original block index, due to multiple bels at the same tile this might
|
|
|
|
// not be the same as the nextpnr z-coordinate
|
|
|
|
int block_index;
|
2021-05-03 03:40:27 +08:00
|
|
|
std::unordered_map<IdString, PinInfo> pins;
|
|
|
|
// Info for different kinds of bels
|
|
|
|
union
|
|
|
|
{
|
|
|
|
// This enables fast lookup of the associated ALM, etc
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
uint32_t lab; // index into the list of LABs
|
|
|
|
uint8_t alm; // ALM index inside LAB
|
|
|
|
uint8_t idx; // LUT or FF index inside ALM
|
2021-05-03 04:41:09 +08:00
|
|
|
} lab_data;
|
2021-05-03 03:40:27 +08:00
|
|
|
};
|
2021-02-03 08:12:14 +08:00
|
|
|
};
|
|
|
|
|
2021-05-01 22:25:43 +08:00
|
|
|
// We maintain our own wire data based on mistral's. This gets us the bidirectional linking that nextpnr needs,
|
|
|
|
// and also makes it easy to add wires and pips for our own purposes like LAB internal routing, global clock
|
|
|
|
// sources, etc.
|
2021-05-01 20:12:33 +08:00
|
|
|
struct WireInfo
|
|
|
|
{
|
|
|
|
// name_override is only used for nextpnr-created wires
|
|
|
|
// otherwise; this is empty and a name is created according to mistral rules
|
|
|
|
IdString name_override;
|
|
|
|
|
|
|
|
// these are transformed on-the-fly to PipId by the iterator, to save space (WireId is half the size of PipId)
|
|
|
|
std::vector<WireId> wires_downhill;
|
|
|
|
std::vector<WireId> wires_uphill;
|
|
|
|
|
|
|
|
std::vector<BelPin> bel_pins;
|
|
|
|
|
|
|
|
// flags for special wires (currently unused)
|
|
|
|
uint64_t flags;
|
|
|
|
};
|
|
|
|
|
2021-05-01 21:06:13 +08:00
|
|
|
// This transforms a WireIds, and adds the mising half of the pair to create a PipId
|
|
|
|
using WireVecIterator = std::vector<WireId>::const_iterator;
|
|
|
|
struct UpDownhillPipIterator
|
|
|
|
{
|
|
|
|
WireVecIterator base;
|
|
|
|
WireId other_wire;
|
|
|
|
bool is_uphill;
|
|
|
|
|
|
|
|
UpDownhillPipIterator(WireVecIterator base, WireId other_wire, bool is_uphill)
|
|
|
|
: base(base), other_wire(other_wire), is_uphill(is_uphill){};
|
|
|
|
|
|
|
|
bool operator!=(const UpDownhillPipIterator &other) { return base != other.base; }
|
|
|
|
UpDownhillPipIterator operator++()
|
|
|
|
{
|
|
|
|
++base;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
UpDownhillPipIterator operator++(int)
|
|
|
|
{
|
|
|
|
UpDownhillPipIterator prior(*this);
|
|
|
|
++(*this);
|
|
|
|
return prior;
|
|
|
|
}
|
|
|
|
PipId operator*() { return is_uphill ? PipId(base->node, other_wire.node) : PipId(other_wire.node, base->node); }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct UpDownhillPipRange
|
|
|
|
{
|
|
|
|
UpDownhillPipIterator b, e;
|
|
|
|
|
|
|
|
UpDownhillPipRange(const std::vector<WireId> &v, WireId other_wire, bool is_uphill)
|
|
|
|
: b(v.cbegin(), other_wire, is_uphill), e(v.cend(), other_wire, is_uphill){};
|
|
|
|
|
|
|
|
UpDownhillPipIterator begin() const { return b; }
|
|
|
|
UpDownhillPipIterator end() const { return e; }
|
|
|
|
};
|
|
|
|
|
2021-05-01 22:25:43 +08:00
|
|
|
// This iterates over the list of wires, and for each wire yields its uphill pips, as an efficient way of going over
|
|
|
|
// all the pips in the device
|
|
|
|
using WireMapIterator = std::unordered_map<WireId, WireInfo>::const_iterator;
|
|
|
|
struct AllPipIterator
|
|
|
|
{
|
|
|
|
WireMapIterator base, end;
|
|
|
|
int uphill_idx;
|
|
|
|
|
|
|
|
AllPipIterator(WireMapIterator base, WireMapIterator end, int uphill_idx)
|
|
|
|
: base(base), end(end), uphill_idx(uphill_idx){};
|
|
|
|
|
|
|
|
bool operator!=(const AllPipIterator &other) { return base != other.base || uphill_idx != other.uphill_idx; }
|
|
|
|
AllPipIterator operator++()
|
|
|
|
{
|
|
|
|
// Increment uphill list index by one
|
|
|
|
++uphill_idx;
|
|
|
|
// We've reached the end of the current wire. Keep incrementing the wire of interest until we find one with
|
|
|
|
// uphill pips, or we reach the end of the list of wires
|
|
|
|
while (base != end && uphill_idx >= int(base->second.wires_uphill.size())) {
|
|
|
|
uphill_idx = 0;
|
|
|
|
++base;
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
AllPipIterator operator++(int)
|
|
|
|
{
|
|
|
|
AllPipIterator prior(*this);
|
|
|
|
++(*this);
|
|
|
|
return prior;
|
|
|
|
}
|
|
|
|
PipId operator*() { return PipId(base->second.wires_uphill.at(uphill_idx).node, base->first.node); }
|
|
|
|
};
|
|
|
|
|
|
|
|
struct AllPipRange
|
|
|
|
{
|
|
|
|
AllPipIterator b, e;
|
|
|
|
|
|
|
|
AllPipRange(const std::unordered_map<WireId, WireInfo> &wires)
|
|
|
|
: b(wires.cbegin(), wires.cend(), -1), e(wires.cend(), wires.cend(), 0)
|
|
|
|
{
|
|
|
|
// Starting the begin iterator at index -1 and incrementing it ensures we skip over the first wire if it has no
|
|
|
|
// uphill pips
|
|
|
|
++b;
|
|
|
|
};
|
|
|
|
|
|
|
|
AllPipIterator begin() const { return b; }
|
|
|
|
AllPipIterator end() const { return e; }
|
|
|
|
};
|
|
|
|
|
2021-05-01 21:06:13 +08:00
|
|
|
// This transforms a map to a range of keys, used as the wire iterator
|
|
|
|
template <typename T> struct key_range
|
|
|
|
{
|
|
|
|
key_range(const T &t) : b(t.cbegin()), e(t.cend()){};
|
|
|
|
typename T::const_iterator b, e;
|
|
|
|
|
|
|
|
struct xformed_iterator : public T::const_iterator
|
|
|
|
{
|
|
|
|
explicit xformed_iterator(typename T::const_iterator base) : T::const_iterator(base){};
|
|
|
|
typename T::key_type operator*() { return this->T::const_iterator::operator*().first; }
|
|
|
|
};
|
|
|
|
|
|
|
|
xformed_iterator begin() const { return xformed_iterator(b); }
|
|
|
|
xformed_iterator end() const { return xformed_iterator(e); }
|
|
|
|
};
|
|
|
|
|
|
|
|
using AllWireRange = key_range<std::unordered_map<WireId, WireInfo>>;
|
|
|
|
|
2021-05-01 01:40:24 +08:00
|
|
|
struct ArchRanges : BaseArchRanges
|
|
|
|
{
|
|
|
|
using ArchArgsT = ArchArgs;
|
|
|
|
// Bels
|
2021-05-03 22:12:24 +08:00
|
|
|
using AllBelsRangeT = const std::vector<BelId> &;
|
2021-05-01 01:40:24 +08:00
|
|
|
using TileBelsRangeT = std::vector<BelId>;
|
|
|
|
using BelPinsRangeT = std::vector<IdString>;
|
|
|
|
// Wires
|
2021-05-01 21:06:13 +08:00
|
|
|
using AllWiresRangeT = AllWireRange;
|
|
|
|
using DownhillPipRangeT = UpDownhillPipRange;
|
|
|
|
using UphillPipRangeT = UpDownhillPipRange;
|
2021-05-01 20:40:45 +08:00
|
|
|
using WireBelPinRangeT = const std::vector<BelPin> &;
|
2021-05-01 01:40:24 +08:00
|
|
|
// Pips
|
2021-05-01 22:25:43 +08:00
|
|
|
using AllPipsRangeT = AllPipRange;
|
2021-05-01 01:40:24 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Arch : BaseArch<ArchRanges>
|
2020-06-13 05:09:46 +08:00
|
|
|
{
|
|
|
|
ArchArgs args;
|
2021-05-01 01:40:24 +08:00
|
|
|
mistral::CycloneV *cyclonev;
|
2021-01-07 00:22:17 +08:00
|
|
|
|
2020-06-13 05:09:46 +08:00
|
|
|
Arch(ArchArgs args);
|
2021-05-01 20:40:45 +08:00
|
|
|
ArchArgs archArgs() const { return args; }
|
2020-06-13 05:09:46 +08:00
|
|
|
|
2021-05-01 01:40:24 +08:00
|
|
|
std::string getChipName() const override { return std::string{"TODO: getChipName"}; }
|
2020-06-13 05:09:46 +08:00
|
|
|
// -------------------------------------------------
|
|
|
|
|
2021-05-01 01:40:24 +08:00
|
|
|
int getGridDimX() const override { return cyclonev->get_tile_sx(); }
|
|
|
|
int getGridDimY() const override { return cyclonev->get_tile_sy(); }
|
|
|
|
int getTileBelDimZ(int x, int y) const override; // arch.cc
|
2021-05-01 21:55:33 +08:00
|
|
|
char getNameDelimiter() const override { return '.'; }
|
2020-06-13 05:09:46 +08:00
|
|
|
|
|
|
|
// -------------------------------------------------
|
|
|
|
|
2021-05-01 01:40:24 +08:00
|
|
|
BelId getBelByName(IdStringList name) const override; // arch.cc
|
|
|
|
IdStringList getBelName(BelId bel) const override; // arch.cc
|
2021-05-03 22:12:24 +08:00
|
|
|
const std::vector<BelId> &getBels() const override { return all_bels; }
|
2021-05-01 01:40:24 +08:00
|
|
|
std::vector<BelId> getBelsByTile(int x, int y) const override;
|
|
|
|
Loc getBelLocation(BelId bel) const override
|
|
|
|
{
|
|
|
|
return Loc(CycloneV::pos2x(bel.pos), CycloneV::pos2y(bel.pos), bel.z);
|
|
|
|
}
|
2021-05-01 21:55:33 +08:00
|
|
|
BelId getBelByLocation(Loc loc) const override
|
|
|
|
{
|
2021-05-03 22:12:24 +08:00
|
|
|
if (loc.x < 0 || loc.x >= cyclonev->get_tile_sx())
|
|
|
|
return BelId();
|
|
|
|
if (loc.y < 0 || loc.y >= cyclonev->get_tile_sy())
|
|
|
|
return BelId();
|
|
|
|
auto &bels = bels_by_tile.at(pos2idx(loc.x, loc.y));
|
|
|
|
if (loc.z < 0 || loc.z >= int(bels.size()))
|
2021-05-01 21:55:33 +08:00
|
|
|
return BelId();
|
2021-05-03 22:12:24 +08:00
|
|
|
return BelId(CycloneV::xy2pos(loc.x, loc.y), loc.z);
|
2021-05-01 21:55:33 +08:00
|
|
|
}
|
2021-05-01 01:40:24 +08:00
|
|
|
IdString getBelType(BelId bel) const override; // arch.cc
|
2021-05-03 22:12:24 +08:00
|
|
|
WireId getBelPinWire(BelId bel, IdString pin) const override
|
|
|
|
{
|
|
|
|
auto &pins = bel_data(bel).pins;
|
|
|
|
auto found = pins.find(pin);
|
|
|
|
if (found == pins.end())
|
|
|
|
return WireId();
|
|
|
|
else
|
|
|
|
return found->second.wire;
|
|
|
|
}
|
|
|
|
PortType getBelPinType(BelId bel, IdString pin) const override { return bel_data(bel).pins.at(pin).dir; }
|
|
|
|
std::vector<IdString> getBelPins(BelId bel) const override;
|
2020-06-13 05:09:46 +08:00
|
|
|
|
|
|
|
// -------------------------------------------------
|
|
|
|
|
2021-05-01 21:55:33 +08:00
|
|
|
WireId getWireByName(IdStringList name) const override;
|
|
|
|
IdStringList getWireName(WireId wire) const override;
|
2021-05-01 20:40:45 +08:00
|
|
|
DelayQuad getWireDelay(WireId wire) const override { return DelayQuad(0); }
|
|
|
|
const std::vector<BelPin> &getWireBelPins(WireId wire) const override { return empty_belpin_list; }
|
2021-05-01 21:06:13 +08:00
|
|
|
AllWireRange getWires() const override { return AllWireRange(wires); }
|
2020-06-13 05:09:46 +08:00
|
|
|
|
|
|
|
// -------------------------------------------------
|
|
|
|
|
2021-05-01 21:55:33 +08:00
|
|
|
PipId getPipByName(IdStringList name) const override;
|
2021-05-01 22:25:43 +08:00
|
|
|
AllPipRange getPips() const override { return AllPipRange(wires); }
|
2021-05-01 20:40:45 +08:00
|
|
|
Loc getPipLocation(PipId pip) const override { return Loc(0, 0, 0); }
|
2021-05-01 21:55:33 +08:00
|
|
|
IdStringList getPipName(PipId pip) const override;
|
2021-05-01 20:12:33 +08:00
|
|
|
WireId getPipSrcWire(PipId pip) const override { return WireId(pip.src); };
|
|
|
|
WireId getPipDstWire(PipId pip) const override { return WireId(pip.dst); };
|
2021-05-01 20:40:45 +08:00
|
|
|
DelayQuad getPipDelay(PipId pip) const override { return DelayQuad(0); }
|
2021-05-01 21:06:13 +08:00
|
|
|
UpDownhillPipRange getPipsDownhill(WireId wire) const override
|
|
|
|
{
|
|
|
|
return UpDownhillPipRange(wires.at(wire).wires_downhill, wire, false);
|
|
|
|
}
|
|
|
|
UpDownhillPipRange getPipsUphill(WireId wire) const override
|
|
|
|
{
|
|
|
|
return UpDownhillPipRange(wires.at(wire).wires_uphill, wire, true);
|
|
|
|
}
|
2020-06-13 05:09:46 +08:00
|
|
|
|
|
|
|
// -------------------------------------------------
|
|
|
|
|
2021-05-01 20:40:45 +08:00
|
|
|
delay_t estimateDelay(WireId src, WireId dst) const override { return 100; };
|
|
|
|
delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const override { return 100; };
|
|
|
|
delay_t getDelayEpsilon() const override { return 10; };
|
|
|
|
delay_t getRipupDelayPenalty() const override { return 100; };
|
|
|
|
float getDelayNS(delay_t v) const override { return float(v) / 1000.0f; };
|
|
|
|
delay_t getDelayFromNS(float ns) const override { return delay_t(ns * 1000.0f); };
|
|
|
|
uint32_t getDelayChecksum(delay_t v) const override { return v; };
|
2020-06-13 05:09:46 +08:00
|
|
|
|
2021-05-01 20:40:45 +08:00
|
|
|
ArcBounds getRouteBoundingBox(WireId src, WireId dst) const override { return ArcBounds(); }
|
2020-06-13 05:09:46 +08:00
|
|
|
|
|
|
|
// -------------------------------------------------
|
|
|
|
|
2021-05-01 01:40:24 +08:00
|
|
|
bool pack() override;
|
|
|
|
bool place() override;
|
|
|
|
bool route() override;
|
2020-06-13 05:09:46 +08:00
|
|
|
|
2021-05-03 03:40:27 +08:00
|
|
|
// -------------------------------------------------
|
|
|
|
// Functions for device setup
|
|
|
|
|
2021-05-03 04:41:09 +08:00
|
|
|
BelId add_bel(int x, int y, IdString name, IdString type);
|
2021-05-03 03:40:27 +08:00
|
|
|
WireId add_wire(int x, int y, IdString name, uint64_t flags = 0);
|
|
|
|
PipId add_pip(WireId src, WireId dst);
|
|
|
|
|
|
|
|
void add_bel_pin(BelId bel, IdString pin, PortType dir, WireId wire);
|
|
|
|
|
2021-05-03 04:41:09 +08:00
|
|
|
WireId get_port(CycloneV::block_type_t bt, int x, int y, int bi, CycloneV::port_type_t port, int pi = -1) const
|
|
|
|
{
|
|
|
|
return WireId(cyclonev->pnode_to_rnode(CycloneV::pnode(bt, x, y, port, bi, pi)));
|
|
|
|
}
|
|
|
|
|
2021-05-03 03:40:27 +08:00
|
|
|
void create_lab(int x, int y);
|
|
|
|
void create_gpio(int x, int y);
|
|
|
|
|
2020-06-13 05:09:46 +08:00
|
|
|
// -------------------------------------------------
|
|
|
|
|
|
|
|
static const std::string defaultPlacer;
|
|
|
|
static const std::vector<std::string> availablePlacers;
|
|
|
|
static const std::string defaultRouter;
|
|
|
|
static const std::vector<std::string> availableRouters;
|
2021-05-01 20:40:45 +08:00
|
|
|
|
2021-05-01 21:06:13 +08:00
|
|
|
std::unordered_map<WireId, WireInfo> wires;
|
|
|
|
|
2021-05-03 04:41:09 +08:00
|
|
|
// List of LABs
|
|
|
|
std::vector<LABInfo> labs;
|
|
|
|
|
2021-05-01 20:40:45 +08:00
|
|
|
// WIP to link without failure
|
|
|
|
std::vector<BelPin> empty_belpin_list;
|
2021-05-01 21:55:33 +08:00
|
|
|
|
|
|
|
// Conversion between numbers and rnode types and IdString, for fast wire name implementation
|
|
|
|
std::vector<IdString> int2id;
|
|
|
|
std::unordered_map<IdString, int> id2int;
|
|
|
|
|
|
|
|
std::vector<IdString> rn_t2id;
|
|
|
|
std::unordered_map<IdString, CycloneV::rnode_type_t> id2rn_t;
|
|
|
|
|
|
|
|
// This structure is only used for nextpnr-created wires
|
|
|
|
std::unordered_map<IdStringList, WireId> npnr_wirebyname;
|
2021-05-03 22:12:24 +08:00
|
|
|
|
|
|
|
std::vector<std::vector<BelInfo>> bels_by_tile;
|
|
|
|
std::vector<BelId> all_bels;
|
|
|
|
|
|
|
|
size_t pos2idx(int x, int y) const
|
|
|
|
{
|
|
|
|
NPNR_ASSERT(x >= 0 && x < int(cyclonev->get_tile_sx()));
|
|
|
|
NPNR_ASSERT(y >= 0 && y < int(cyclonev->get_tile_sy()));
|
|
|
|
return y * cyclonev->get_tile_sx() + x;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t pos2idx(CycloneV::pos_t pos) const { return pos2idx(CycloneV::pos2x(pos), CycloneV::pos2y(pos)); }
|
|
|
|
|
|
|
|
BelInfo &bel_data(BelId bel) { return bels_by_tile.at(pos2idx(bel.pos)).at(bel.z); }
|
|
|
|
const BelInfo &bel_data(BelId bel) const { return bels_by_tile.at(pos2idx(bel.pos)).at(bel.z); }
|
2020-06-13 05:09:46 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
NEXTPNR_NAMESPACE_END
|
2021-05-01 01:40:24 +08:00
|
|
|
|
|
|
|
#endif
|