1071 lines
34 KiB
C++
1071 lines
34 KiB
C++
/*
|
|
* nextpnr -- Next Generation Place and Route
|
|
*
|
|
* Copyright (C) 2018 Claire Xenia Wolf <claire@yosyshq.com>
|
|
* Copyright (C) 2021 William D. Jones <wjones@wdj-consulting.com>
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#ifndef MACHXO2_ARCH_H
|
|
#define MACHXO2_ARCH_H
|
|
|
|
#include <cstdint>
|
|
#include <set>
|
|
|
|
#include "base_arch.h"
|
|
#include "nextpnr_types.h"
|
|
#include "relptr.h"
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
/**** Everything in this section must be kept in sync with chipdb.py ****/
|
|
|
|
// FIXME: All "rel locs" are actually absolute, naming typo in facade_import.
|
|
// Does not affect runtime functionality.
|
|
|
|
NPNR_PACKED_STRUCT(struct BelWirePOD {
|
|
LocationPOD rel_wire_loc;
|
|
int32_t wire_index;
|
|
int32_t port;
|
|
int32_t type;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct BelInfoPOD {
|
|
RelPtr<char> name;
|
|
int32_t type;
|
|
int32_t z;
|
|
RelSlice<BelWirePOD> bel_wires;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct BelPortPOD {
|
|
LocationPOD rel_bel_loc;
|
|
int32_t bel_index;
|
|
int32_t port;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct PipInfoPOD {
|
|
LocationPOD src, dst;
|
|
int16_t src_idx, dst_idx;
|
|
int16_t timing_class;
|
|
int8_t tile_type;
|
|
int8_t pip_type;
|
|
int16_t lutperm_flags;
|
|
int16_t padding;
|
|
});
|
|
|
|
inline bool is_lutperm_pip(int16_t flags) { return flags & 0x4000; }
|
|
inline uint8_t lutperm_lut(int16_t flags) { return (flags >> 4) & 0x7; }
|
|
inline uint8_t lutperm_out(int16_t flags) { return (flags >> 2) & 0x3; }
|
|
inline uint8_t lutperm_in(int16_t flags) { return flags & 0x3; }
|
|
|
|
NPNR_PACKED_STRUCT(struct PipLocatorPOD {
|
|
LocationPOD rel_loc;
|
|
int32_t index;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct WireInfoPOD {
|
|
RelPtr<char> name;
|
|
int16_t type;
|
|
int16_t tile_wire;
|
|
RelSlice<PipLocatorPOD> pips_uphill, pips_downhill;
|
|
RelSlice<BelPortPOD> bel_pins;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct TileTypePOD {
|
|
RelSlice<BelInfoPOD> bel_data;
|
|
RelSlice<WireInfoPOD> wire_data;
|
|
RelSlice<PipInfoPOD> pip_data;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct PIOInfoPOD {
|
|
LocationPOD abs_loc;
|
|
int32_t bel_index;
|
|
RelPtr<char> function_name;
|
|
int16_t bank;
|
|
int16_t dqsgroup;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct PackagePinPOD {
|
|
RelPtr<char> name;
|
|
LocationPOD abs_loc;
|
|
int32_t bel_index;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct PackageInfoPOD {
|
|
RelPtr<char> name;
|
|
RelSlice<PackagePinPOD> pin_data;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct TileNamePOD {
|
|
RelPtr<char> name;
|
|
int16_t type_idx;
|
|
int16_t padding;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct TileInfoPOD { RelSlice<TileNamePOD> tile_names; });
|
|
|
|
NPNR_PACKED_STRUCT(struct CellPropDelayPOD {
|
|
int32_t from_port;
|
|
int32_t to_port;
|
|
int32_t min_delay;
|
|
int32_t max_delay;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct CellSetupHoldPOD {
|
|
int32_t sig_port;
|
|
int32_t clock_port;
|
|
int32_t min_setup;
|
|
int32_t max_setup;
|
|
int32_t min_hold;
|
|
int32_t max_hold;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct CellTimingPOD {
|
|
int32_t cell_type;
|
|
RelSlice<CellPropDelayPOD> prop_delays;
|
|
RelSlice<CellSetupHoldPOD> setup_holds;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct PipDelayPOD {
|
|
int32_t min_base_delay;
|
|
int32_t max_base_delay;
|
|
int32_t min_fanout_adder;
|
|
int32_t max_fanout_adder;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct SpeedGradePOD {
|
|
RelSlice<CellTimingPOD> cell_timings;
|
|
RelSlice<PipDelayPOD> pip_classes;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct PackageSupportedPOD {
|
|
RelPtr<char> name;
|
|
RelPtr<char> short_name;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct SuffixeSupportedPOD { RelPtr<char> suffix; });
|
|
|
|
NPNR_PACKED_STRUCT(struct SpeedSupportedPOD {
|
|
int16_t speed;
|
|
int16_t index;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct VariantInfoPOD {
|
|
RelPtr<char> name;
|
|
RelSlice<PackageSupportedPOD> packages;
|
|
RelSlice<SpeedSupportedPOD> speeds;
|
|
RelSlice<SuffixeSupportedPOD> suffixes;
|
|
});
|
|
|
|
NPNR_PACKED_STRUCT(struct SpineInfoPOD { int32_t row; });
|
|
|
|
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
|
|
RelPtr<char> family;
|
|
RelPtr<char> device_name;
|
|
int32_t width, height;
|
|
int32_t num_tiles;
|
|
int32_t const_id_count;
|
|
RelSlice<TileTypePOD> tiles;
|
|
RelSlice<RelPtr<char>> tiletype_names;
|
|
RelSlice<PackageInfoPOD> package_info;
|
|
RelSlice<PIOInfoPOD> pio_info;
|
|
RelSlice<TileInfoPOD> tile_info;
|
|
RelSlice<VariantInfoPOD> variants;
|
|
RelSlice<SpineInfoPOD> spines;
|
|
RelSlice<SpeedGradePOD> speed_grades;
|
|
});
|
|
|
|
/************************ End of chipdb section. ************************/
|
|
|
|
struct BelIterator
|
|
{
|
|
const ChipInfoPOD *chip;
|
|
int cursor_index;
|
|
int cursor_tile;
|
|
|
|
BelIterator operator++()
|
|
{
|
|
cursor_index++;
|
|
while (cursor_tile < chip->num_tiles && cursor_index >= chip->tiles[cursor_tile].bel_data.ssize()) {
|
|
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 % chip->width;
|
|
ret.location.y = cursor_tile / chip->width;
|
|
ret.index = cursor_index;
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
struct BelRange
|
|
{
|
|
BelIterator b, e;
|
|
BelIterator begin() const { return b; }
|
|
BelIterator end() const { return e; }
|
|
};
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
struct BelPinIterator
|
|
{
|
|
const BelPortPOD *ptr = nullptr;
|
|
Location wire_loc;
|
|
void operator++() { ptr++; }
|
|
bool operator!=(const BelPinIterator &other) const { return ptr != other.ptr; }
|
|
|
|
BelPin operator*() const
|
|
{
|
|
BelPin ret;
|
|
ret.bel.index = ptr->bel_index;
|
|
ret.bel.location = ptr->rel_bel_loc;
|
|
ret.pin.index = ptr->port;
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
struct BelPinRange
|
|
{
|
|
BelPinIterator b, e;
|
|
BelPinIterator begin() const { return b; }
|
|
BelPinIterator end() const { return e; }
|
|
};
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
struct WireIterator
|
|
{
|
|
const ChipInfoPOD *chip;
|
|
int cursor_index;
|
|
int cursor_tile;
|
|
|
|
WireIterator operator++()
|
|
{
|
|
cursor_index++;
|
|
while (cursor_tile < chip->num_tiles && cursor_index >= chip->tiles[cursor_tile].wire_data.ssize()) {
|
|
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 % chip->width;
|
|
ret.location.y = cursor_tile / chip->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 ChipInfoPOD *chip;
|
|
int cursor_index;
|
|
int cursor_tile;
|
|
|
|
AllPipIterator operator++()
|
|
{
|
|
cursor_index++;
|
|
while (cursor_tile < chip->num_tiles && cursor_index >= chip->tiles[cursor_tile].pip_data.ssize()) {
|
|
cursor_index = 0;
|
|
cursor_tile++;
|
|
}
|
|
return *this;
|
|
}
|
|
AllPipIterator operator++(int)
|
|
{
|
|
AllPipIterator prior(*this);
|
|
++(*this);
|
|
return prior;
|
|
}
|
|
|
|
bool operator!=(const AllPipIterator &other) const
|
|
{
|
|
return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile;
|
|
}
|
|
|
|
bool operator==(const AllPipIterator &other) const
|
|
{
|
|
return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile;
|
|
}
|
|
|
|
PipId operator*() const
|
|
{
|
|
PipId ret;
|
|
ret.location.x = cursor_tile % chip->width;
|
|
ret.location.y = cursor_tile / chip->width;
|
|
ret.index = cursor_index;
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
struct AllPipRange
|
|
{
|
|
AllPipIterator b, e;
|
|
AllPipIterator begin() const { return b; }
|
|
AllPipIterator end() const { return e; }
|
|
};
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
struct PipIterator
|
|
{
|
|
|
|
const PipLocatorPOD *cursor = nullptr;
|
|
Location wire_loc;
|
|
|
|
void operator++() { cursor++; }
|
|
bool operator!=(const PipIterator &other) const { return cursor != other.cursor; }
|
|
|
|
PipId operator*() const
|
|
{
|
|
PipId ret;
|
|
ret.index = cursor->index;
|
|
ret.location = cursor->rel_loc;
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
struct PipRange
|
|
{
|
|
PipIterator b, e;
|
|
PipIterator begin() const { return b; }
|
|
PipIterator end() const { return e; }
|
|
};
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
struct ArchArgs
|
|
{
|
|
std::string device;
|
|
};
|
|
|
|
struct DelayKey
|
|
{
|
|
IdString celltype, from, to;
|
|
inline bool operator==(const DelayKey &other) const
|
|
{
|
|
return celltype == other.celltype && from == other.from && to == other.to;
|
|
}
|
|
unsigned int hash() const { return mkhash(celltype.hash(), mkhash(from.hash(), to.hash())); }
|
|
};
|
|
|
|
struct ArchRanges : BaseArchRanges
|
|
{
|
|
using ArchArgsT = ArchArgs;
|
|
// Bels
|
|
using AllBelsRangeT = BelRange;
|
|
using TileBelsRangeT = BelRange;
|
|
using BelPinsRangeT = std::vector<IdString>;
|
|
// Wires
|
|
using AllWiresRangeT = WireRange;
|
|
using DownhillPipRangeT = PipRange;
|
|
using UphillPipRangeT = PipRange;
|
|
using WireBelPinRangeT = BelPinRange;
|
|
// Pips
|
|
using AllPipsRangeT = AllPipRange;
|
|
};
|
|
|
|
struct Arch : BaseArch<ArchRanges>
|
|
{
|
|
const ChipInfoPOD *chip_info;
|
|
const PackageInfoPOD *package_info;
|
|
const SpeedGradePOD *speed_grade;
|
|
|
|
const char *package_name;
|
|
const char *device_name;
|
|
int device_speed;
|
|
|
|
mutable dict<IdStringList, PipId> pip_by_name;
|
|
|
|
enum class LutPermRule
|
|
{
|
|
NONE,
|
|
CARRY,
|
|
ALL,
|
|
};
|
|
std::vector<LutPermRule> lutperm_allowed;
|
|
bool disable_router_lutperm = false;
|
|
|
|
// For fast, incremental validity checking of split SLICE
|
|
|
|
// BEL z-position lookup, x-ored with (index in tile) << 2
|
|
enum LogicBELType
|
|
{
|
|
BEL_COMB = 0,
|
|
BEL_FF = 1,
|
|
BEL_RAMW = 2
|
|
};
|
|
static const int lc_idx_shift = 2;
|
|
|
|
struct LogicTileStatus
|
|
{
|
|
// Per-SLICE valid and dirty bits
|
|
struct SliceStatus
|
|
{
|
|
bool valid = true, dirty = true;
|
|
} slices[4];
|
|
// Per-tile legality check for control set legality
|
|
bool tile_valid = true;
|
|
bool tile_dirty = true;
|
|
// Fast index from z-pos to cell
|
|
std::array<CellInfo *, 8 * (1 << lc_idx_shift)> cells;
|
|
};
|
|
|
|
struct TileStatus
|
|
{
|
|
std::vector<CellInfo *> boundcells;
|
|
LogicTileStatus *lts = nullptr;
|
|
// TODO: use similar mechanism for DSP legality checking
|
|
~TileStatus() { delete lts; }
|
|
};
|
|
|
|
// faster replacements for base_pip2net, base_wire2net
|
|
// indexed by get_pip_vecidx()
|
|
std::vector<NetInfo *> pip2net;
|
|
// indexed by get_wire_vecidx()
|
|
std::vector<NetInfo *> wire2net;
|
|
std::vector<int> wire_fanout;
|
|
// We record the index=0 offset into pip2net for each tile, allowing us to
|
|
// calculate any PipId's offset from pip.index and pip.location
|
|
std::vector<int32_t> pip_tile_vecidx;
|
|
std::vector<int32_t> wire_tile_vecidx;
|
|
|
|
// fast access to X and Y IdStrings for building object names
|
|
std::vector<IdString> x_ids, y_ids;
|
|
// inverse of the above for name->object mapping
|
|
dict<IdString, int> id_to_x, id_to_y;
|
|
|
|
ArchArgs args;
|
|
Arch(ArchArgs args);
|
|
|
|
static void list_devices();
|
|
|
|
std::string getChipName() const override;
|
|
std::string get_full_chip_name() const;
|
|
|
|
IdString archId() const override { return id_machxo2; }
|
|
ArchArgs archArgs() const override { return args; }
|
|
IdString archArgsToId(ArchArgs args) const override;
|
|
|
|
// -------------------------------------------------
|
|
|
|
static const int max_loc_bels = 32;
|
|
|
|
int getGridDimX() const override { return chip_info->width; };
|
|
int getGridDimY() const override { return chip_info->height; };
|
|
int getTileBelDimZ(int, int) const override { return max_loc_bels; };
|
|
// TODO: Make more precise? The CENTER MUX having config bits across
|
|
// tiles can complicate this?
|
|
int getTilePipDimZ(int, int) const override { return 2; };
|
|
char getNameDelimiter() const override { return '/'; }
|
|
|
|
// -------------------------------------------------
|
|
|
|
BelId getBelByName(IdStringList name) const override;
|
|
|
|
template <typename Id> const TileTypePOD *tile_info(Id &id) const
|
|
{
|
|
return &(chip_info->tiles[id.location.y * chip_info->width + id.location.x]);
|
|
}
|
|
|
|
template <typename Id> inline int tile_index(Id id) const
|
|
{
|
|
return id.location.y * chip_info->width + id.location.x;
|
|
}
|
|
|
|
IdStringList getBelName(BelId bel) const override
|
|
{
|
|
NPNR_ASSERT(bel != BelId());
|
|
std::array<IdString, 3> ids{x_ids.at(bel.location.x), y_ids.at(bel.location.y),
|
|
id(tile_info(bel)->bel_data[bel.index].name.get())};
|
|
return IdStringList(ids);
|
|
}
|
|
|
|
uint32_t getBelChecksum(BelId bel) const override { return bel.index; }
|
|
|
|
int get_slice_index(int x, int y, int slice) const
|
|
{
|
|
NPNR_ASSERT(slice >= 0 && slice < 4);
|
|
return (y * chip_info->width + x) * 4 + slice;
|
|
}
|
|
|
|
void update_bel(BelId bel, CellInfo *old_cell, CellInfo *new_cell)
|
|
{
|
|
CellInfo *act_cell = (old_cell == nullptr) ? new_cell : old_cell;
|
|
if (act_cell->type.in(id_TRELLIS_FF, id_TRELLIS_COMB, id_TRELLIS_RAMW)) {
|
|
LogicTileStatus *lts = tile_status.at(tile_index(bel)).lts;
|
|
NPNR_ASSERT(lts != nullptr);
|
|
int z = tile_info(bel)->bel_data[bel.index].z;
|
|
lts->slices[(z >> lc_idx_shift) / 2].dirty = true;
|
|
if (act_cell->type == id_TRELLIS_FF)
|
|
lts->tile_dirty = true; // because FF CLK/LSR signals are tile-wide
|
|
if (act_cell->type == id_TRELLIS_COMB && (act_cell->combInfo.flags & ArchCellInfo::COMB_LUTRAM))
|
|
lts->tile_dirty = true; // because RAM shares CLK/LSR signals with FFs
|
|
lts->cells[z] = new_cell;
|
|
}
|
|
}
|
|
|
|
void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) override
|
|
{
|
|
NPNR_ASSERT(bel != BelId());
|
|
auto &slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
|
|
NPNR_ASSERT(slot == nullptr);
|
|
slot = cell;
|
|
cell->bel = bel;
|
|
cell->belStrength = strength;
|
|
if (getBelType(bel) == id_TRELLIS_COMB) {
|
|
int flags = cell->combInfo.flags;
|
|
lutperm_allowed.at(
|
|
get_slice_index(bel.location.x, bel.location.y, (getBelLocation(bel).z >> lc_idx_shift) / 2)) =
|
|
(((flags & ArchCellInfo::COMB_LUTRAM) || (flags & ArchCellInfo::COMB_RAMW_BLOCK))
|
|
? LutPermRule::NONE
|
|
: ((flags & ArchCellInfo::COMB_CARRY) ? LutPermRule::CARRY : LutPermRule::ALL));
|
|
}
|
|
update_bel(bel, nullptr, cell);
|
|
refreshUiBel(bel);
|
|
}
|
|
|
|
void unbindBel(BelId bel) override
|
|
{
|
|
NPNR_ASSERT(bel != BelId());
|
|
auto &slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
|
|
NPNR_ASSERT(slot != nullptr);
|
|
update_bel(bel, slot, nullptr);
|
|
slot->bel = BelId();
|
|
slot->belStrength = STRENGTH_NONE;
|
|
slot = nullptr;
|
|
refreshUiBel(bel);
|
|
}
|
|
|
|
Loc getBelLocation(BelId bel) const override
|
|
{
|
|
NPNR_ASSERT(bel != BelId());
|
|
Loc loc;
|
|
loc.x = bel.location.x;
|
|
loc.y = bel.location.y;
|
|
loc.z = tile_info(bel)->bel_data[bel.index].z;
|
|
return loc;
|
|
}
|
|
|
|
BelId getBelByLocation(Loc loc) const override;
|
|
BelRange getBelsByTile(int x, int y) const override;
|
|
|
|
bool getBelGlobalBuf(BelId bel) const override { return getBelType(bel) == id_DCCA; }
|
|
|
|
bool checkBelAvail(BelId bel) const override
|
|
{
|
|
NPNR_ASSERT(bel != BelId());
|
|
const CellInfo *slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
|
|
return slot == nullptr;
|
|
}
|
|
|
|
CellInfo *getBoundBelCell(BelId bel) const override
|
|
{
|
|
NPNR_ASSERT(bel != BelId());
|
|
CellInfo *slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
|
|
return slot;
|
|
}
|
|
|
|
CellInfo *getConflictingBelCell(BelId bel) const override
|
|
{
|
|
NPNR_ASSERT(bel != BelId());
|
|
CellInfo *slot = tile_status.at(tile_index(bel)).boundcells.at(bel.index);
|
|
return slot;
|
|
}
|
|
|
|
BelRange getBels() const override
|
|
{
|
|
BelRange range;
|
|
range.b.cursor_tile = 0;
|
|
range.b.cursor_index = -1;
|
|
range.b.chip = chip_info;
|
|
++range.b; //-1 and then ++ deals with the case of no Bels in the first tile
|
|
range.e.cursor_tile = chip_info->width * chip_info->height;
|
|
range.e.cursor_index = 0;
|
|
range.e.chip = chip_info;
|
|
return range;
|
|
}
|
|
|
|
IdString getBelType(BelId bel) const override
|
|
{
|
|
NPNR_ASSERT(bel != BelId());
|
|
IdString id;
|
|
id.index = tile_info(bel)->bel_data[bel.index].type;
|
|
return id;
|
|
}
|
|
|
|
WireId getBelPinWire(BelId bel, IdString pin) const override;
|
|
|
|
BelPinRange getWireBelPins(WireId wire) const override
|
|
{
|
|
BelPinRange range;
|
|
NPNR_ASSERT(wire != WireId());
|
|
range.b.ptr = tile_info(wire)->wire_data[wire.index].bel_pins.begin();
|
|
range.b.wire_loc = wire.location;
|
|
range.e.ptr = tile_info(wire)->wire_data[wire.index].bel_pins.end();
|
|
range.e.wire_loc = wire.location;
|
|
return range;
|
|
}
|
|
|
|
std::vector<IdString> getBelPins(BelId bel) const override;
|
|
|
|
// -------------------------------------------------
|
|
|
|
WireId getWireByName(IdStringList name) const override;
|
|
|
|
IdStringList getWireName(WireId wire) const override
|
|
{
|
|
NPNR_ASSERT(wire != WireId());
|
|
std::array<IdString, 3> ids{x_ids.at(wire.location.x), y_ids.at(wire.location.y),
|
|
id(tile_info(wire)->wire_data[wire.index].name.get())};
|
|
return IdStringList(ids);
|
|
}
|
|
|
|
IdString getWireType(WireId wire) const override
|
|
{
|
|
NPNR_ASSERT(wire != WireId());
|
|
IdString id;
|
|
id.index = tile_info(wire)->wire_data[wire.index].type;
|
|
return id;
|
|
}
|
|
|
|
std::vector<std::pair<IdString, std::string>> getWireAttrs(WireId) const override;
|
|
|
|
uint32_t getWireChecksum(WireId wire) const override { return wire.index; }
|
|
|
|
uint32_t get_wire_vecidx(const WireId &e) const
|
|
{
|
|
uint32_t tile = e.location.y * chip_info->width + e.location.x;
|
|
int32_t base = wire_tile_vecidx.at(tile);
|
|
NPNR_ASSERT(base != -1);
|
|
int32_t i = base + e.index;
|
|
return i;
|
|
}
|
|
|
|
void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) override
|
|
{
|
|
NPNR_ASSERT(wire != WireId());
|
|
auto &w2n_entry = wire2net.at(get_wire_vecidx(wire));
|
|
NPNR_ASSERT(w2n_entry == nullptr);
|
|
net->wires[wire].pip = PipId();
|
|
net->wires[wire].strength = strength;
|
|
w2n_entry = net;
|
|
this->refreshUiWire(wire);
|
|
}
|
|
void unbindWire(WireId wire) override
|
|
{
|
|
NPNR_ASSERT(wire != WireId());
|
|
auto &w2n_entry = wire2net.at(get_wire_vecidx(wire));
|
|
NPNR_ASSERT(w2n_entry != nullptr);
|
|
|
|
auto &net_wires = w2n_entry->wires;
|
|
auto it = net_wires.find(wire);
|
|
NPNR_ASSERT(it != net_wires.end());
|
|
|
|
auto pip = it->second.pip;
|
|
if (pip != PipId()) {
|
|
pip2net.at(get_pip_vecidx(pip)) = nullptr;
|
|
wire_fanout[get_wire_vecidx(getPipSrcWire(pip))]--;
|
|
}
|
|
|
|
net_wires.erase(it);
|
|
w2n_entry = nullptr;
|
|
this->refreshUiWire(wire);
|
|
}
|
|
virtual bool checkWireAvail(WireId wire) const override { return getBoundWireNet(wire) == nullptr; }
|
|
NetInfo *getBoundWireNet(WireId wire) const override { return wire2net.at(get_wire_vecidx(wire)); }
|
|
|
|
DelayQuad getWireDelay(WireId wire) const override { return DelayQuad(0); }
|
|
|
|
WireRange getWires() const override
|
|
{
|
|
WireRange range;
|
|
range.b.cursor_tile = 0;
|
|
range.b.cursor_index = -1;
|
|
range.b.chip = chip_info;
|
|
++range.b; //-1 and then ++ deals with the case of no wries in the first tile
|
|
range.e.cursor_tile = chip_info->width * chip_info->height;
|
|
range.e.cursor_index = 0;
|
|
range.e.chip = chip_info;
|
|
return range;
|
|
}
|
|
|
|
IdString get_wire_basename(WireId wire) const { return id(tile_info(wire)->wire_data[wire.index].name.get()); }
|
|
|
|
WireId get_wire_by_loc_basename(Location loc, std::string basename) const
|
|
{
|
|
WireId wireId;
|
|
wireId.location = loc;
|
|
for (int i = 0; i < tile_info(wireId)->wire_data.ssize(); i++) {
|
|
if (tile_info(wireId)->wire_data[i].name.get() == basename) {
|
|
wireId.index = i;
|
|
return wireId;
|
|
}
|
|
}
|
|
return WireId();
|
|
}
|
|
|
|
// -------------------------------------------------
|
|
|
|
PipId getPipByName(IdStringList name) const override;
|
|
IdStringList getPipName(PipId pip) const override;
|
|
|
|
uint32_t getPipChecksum(PipId pip) const override { return pip.index; }
|
|
|
|
uint32_t get_pip_vecidx(const PipId &e) const
|
|
{
|
|
uint32_t tile = e.location.y * chip_info->width + e.location.x;
|
|
int32_t base = pip_tile_vecidx.at(tile);
|
|
NPNR_ASSERT(base != -1);
|
|
int32_t i = base + e.index;
|
|
return i;
|
|
}
|
|
|
|
void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) override
|
|
{
|
|
NPNR_ASSERT(pip != PipId());
|
|
wire_fanout[get_wire_vecidx(getPipSrcWire(pip))]++;
|
|
|
|
auto &p2n_entry = pip2net.at(get_pip_vecidx(pip));
|
|
NPNR_ASSERT(p2n_entry == nullptr);
|
|
p2n_entry = net;
|
|
|
|
WireId dst = this->getPipDstWire(pip);
|
|
auto &w2n_entry = wire2net.at(get_wire_vecidx(dst));
|
|
NPNR_ASSERT(w2n_entry == nullptr);
|
|
w2n_entry = net;
|
|
net->wires[dst].pip = pip;
|
|
net->wires[dst].strength = strength;
|
|
}
|
|
|
|
void unbindPip(PipId pip) override
|
|
{
|
|
NPNR_ASSERT(pip != PipId());
|
|
wire_fanout[get_wire_vecidx(getPipSrcWire(pip))]--;
|
|
|
|
auto &p2n_entry = pip2net.at(get_pip_vecidx(pip));
|
|
NPNR_ASSERT(p2n_entry != nullptr);
|
|
WireId dst = this->getPipDstWire(pip);
|
|
|
|
auto &w2n_entry = wire2net.at(get_wire_vecidx(dst));
|
|
NPNR_ASSERT(w2n_entry != nullptr);
|
|
w2n_entry = nullptr;
|
|
|
|
p2n_entry->wires.erase(dst);
|
|
p2n_entry = nullptr;
|
|
}
|
|
bool is_pip_blocked(PipId pip) const
|
|
{
|
|
auto &pip_data = tile_info(pip)->pip_data[pip.index];
|
|
int lp = pip_data.lutperm_flags;
|
|
if (is_lutperm_pip(lp)) {
|
|
if (disable_router_lutperm)
|
|
return true;
|
|
auto rule = lutperm_allowed.at(get_slice_index(pip.location.x, pip.location.y, lutperm_lut(lp) / 2));
|
|
if (rule == LutPermRule::NONE) {
|
|
// Permutation not allowed
|
|
return true;
|
|
} else if (rule == LutPermRule::CARRY) {
|
|
// Can swap A/B and C/D only
|
|
int i = lutperm_out(lp), j = lutperm_in(lp);
|
|
if ((i / 2) != (j / 2))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
bool checkPipAvail(PipId pip) const override { return (getBoundPipNet(pip) == nullptr) && !is_pip_blocked(pip); }
|
|
bool checkPipAvailForNet(PipId pip, const NetInfo *net) const override
|
|
{
|
|
NetInfo *bound_net = getBoundPipNet(pip);
|
|
return (bound_net == nullptr || bound_net == net) && !is_pip_blocked(pip);
|
|
}
|
|
NetInfo *getBoundPipNet(PipId pip) const override { return pip2net.at(get_pip_vecidx(pip)); }
|
|
|
|
AllPipRange getPips() const override
|
|
{
|
|
AllPipRange range;
|
|
range.b.cursor_tile = 0;
|
|
range.b.cursor_index = -1;
|
|
range.b.chip = chip_info;
|
|
++range.b; //-1 and then ++ deals with the case of no Bels in the first tile
|
|
range.e.cursor_tile = chip_info->width * chip_info->height;
|
|
range.e.cursor_index = 0;
|
|
range.e.chip = chip_info;
|
|
return range;
|
|
}
|
|
|
|
WireId getPipSrcWire(PipId pip) const override
|
|
{
|
|
WireId wire;
|
|
NPNR_ASSERT(pip != PipId());
|
|
wire.index = tile_info(pip)->pip_data[pip.index].src_idx;
|
|
wire.location = tile_info(pip)->pip_data[pip.index].src;
|
|
return wire;
|
|
}
|
|
|
|
WireId getPipDstWire(PipId pip) const override
|
|
{
|
|
WireId wire;
|
|
NPNR_ASSERT(pip != PipId());
|
|
wire.index = tile_info(pip)->pip_data[pip.index].dst_idx;
|
|
wire.location = tile_info(pip)->pip_data[pip.index].dst;
|
|
return wire;
|
|
}
|
|
|
|
DelayQuad getPipDelay(PipId pip) const override
|
|
{
|
|
NPNR_ASSERT(pip != PipId());
|
|
int fanout = wire_fanout[get_wire_vecidx(getPipSrcWire(pip))];
|
|
delay_t min_dly =
|
|
speed_grade->pip_classes[tile_info(pip)->pip_data[pip.index].timing_class].min_base_delay +
|
|
fanout * speed_grade->pip_classes[tile_info(pip)->pip_data[pip.index].timing_class].min_fanout_adder;
|
|
delay_t max_dly =
|
|
speed_grade->pip_classes[tile_info(pip)->pip_data[pip.index].timing_class].max_base_delay +
|
|
fanout * speed_grade->pip_classes[tile_info(pip)->pip_data[pip.index].timing_class].max_fanout_adder;
|
|
return DelayQuad(min_dly, max_dly);
|
|
}
|
|
|
|
PipRange getPipsDownhill(WireId wire) const override
|
|
{
|
|
PipRange range;
|
|
NPNR_ASSERT(wire != WireId());
|
|
range.b.cursor = tile_info(wire)->wire_data[wire.index].pips_downhill.get();
|
|
range.b.wire_loc = wire.location;
|
|
range.e.cursor = range.b.cursor + tile_info(wire)->wire_data[wire.index].pips_downhill.size();
|
|
range.e.wire_loc = wire.location;
|
|
return range;
|
|
}
|
|
|
|
PipRange getPipsUphill(WireId wire) const override
|
|
{
|
|
PipRange range;
|
|
NPNR_ASSERT(wire != WireId());
|
|
range.b.cursor = tile_info(wire)->wire_data[wire.index].pips_uphill.get();
|
|
range.b.wire_loc = wire.location;
|
|
range.e.cursor = range.b.cursor + tile_info(wire)->wire_data[wire.index].pips_uphill.size();
|
|
range.e.wire_loc = wire.location;
|
|
return range;
|
|
}
|
|
|
|
std::string get_pip_tilename(PipId pip) const
|
|
{
|
|
auto &tileloc = chip_info->tile_info[pip.location.y * chip_info->width + pip.location.x];
|
|
for (auto &tn : tileloc.tile_names) {
|
|
if (tn.type_idx == tile_info(pip)->pip_data[pip.index].tile_type)
|
|
return tn.name.get();
|
|
}
|
|
NPNR_ASSERT_FALSE("failed to find Pip tile");
|
|
}
|
|
|
|
std::string get_pip_tiletype(PipId pip) const
|
|
{
|
|
return chip_info->tiletype_names[tile_info(pip)->pip_data[pip.index].tile_type].get();
|
|
}
|
|
|
|
Loc getPipLocation(PipId pip) const override
|
|
{
|
|
Loc loc;
|
|
loc.x = pip.location.x;
|
|
loc.y = pip.location.y;
|
|
loc.z = 0;
|
|
return loc;
|
|
}
|
|
|
|
int8_t get_pip_class(PipId pip) const { return tile_info(pip)->pip_data[pip.index].pip_type; }
|
|
|
|
BelId get_package_pin_bel(const std::string &pin) const;
|
|
// std::string get_bel_package_pin(BelId bel) const;
|
|
// int get_pio_bel_bank(BelId bel) const;
|
|
// For getting GCLK, PLL, Vref, etc, pins
|
|
// std::string get_pio_function_name(BelId bel) const;
|
|
// BelId get_pio_by_function_name(const std::string &name) const;
|
|
|
|
PortType getBelPinType(BelId bel, IdString pin) const override;
|
|
|
|
// -------------------------------------------------
|
|
|
|
GroupId getGroupByName(IdStringList name) const override;
|
|
IdStringList getGroupName(GroupId group) const override;
|
|
std::vector<GroupId> getGroups() const override;
|
|
std::vector<BelId> getGroupBels(GroupId group) const override;
|
|
std::vector<WireId> getGroupWires(GroupId group) const override;
|
|
std::vector<PipId> getGroupPips(GroupId group) const override;
|
|
std::vector<GroupId> getGroupGroups(GroupId group) const override;
|
|
|
|
// -------------------------------------------------
|
|
|
|
delay_t estimateDelay(WireId src, WireId dst) const override;
|
|
BoundingBox getRouteBoundingBox(WireId src, WireId dst) const override;
|
|
delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override;
|
|
delay_t getDelayEpsilon() const override { return 20; }
|
|
delay_t getRipupDelayPenalty() const override;
|
|
float getDelayNS(delay_t v) const override { return v * 0.001; }
|
|
delay_t getDelayFromNS(float ns) const override { return delay_t(ns * 1000); }
|
|
uint32_t getDelayChecksum(delay_t v) const override { return v; }
|
|
|
|
// -------------------------------------------------
|
|
|
|
bool pack() override;
|
|
bool place() override;
|
|
bool route() override;
|
|
|
|
// -------------------------------------------------
|
|
|
|
std::vector<GraphicElement> getDecalGraphics(DecalId decal) const override;
|
|
|
|
DecalXY getBelDecal(BelId bel) const override;
|
|
DecalXY getWireDecal(WireId wire) const override;
|
|
DecalXY getPipDecal(PipId pip) const override;
|
|
DecalXY getGroupDecal(GroupId group) const override;
|
|
|
|
// -------------------------------------------------
|
|
|
|
// 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, DelayQuad &delay) const override;
|
|
// 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 override;
|
|
// Get the TimingClockingInfo of a port
|
|
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const override;
|
|
|
|
bool get_delay_from_tmg_db(IdString tctype, IdString from, IdString to, DelayQuad &delay) const;
|
|
void get_setuphold_from_tmg_db(IdString tctype, IdString clock, IdString port, DelayPair &setup,
|
|
DelayPair &hold) const;
|
|
|
|
// -------------------------------------------------
|
|
// Placement validity checks
|
|
bool isBelLocationValid(BelId bel, bool explain_invalid = false) const override;
|
|
|
|
// Helper function for above
|
|
bool slices_compatible(LogicTileStatus *lts) const;
|
|
|
|
void assign_arch_info_for_cell(CellInfo *ci);
|
|
void assignArchInfo() override;
|
|
|
|
std::vector<std::pair<std::string, std::string>> get_tiles_at_loc(int row, int col);
|
|
std::string get_tile_by_type_loc(int row, int col, std::string type) const
|
|
{
|
|
auto &tileloc = chip_info->tile_info[row * chip_info->width + col];
|
|
for (auto &tn : tileloc.tile_names) {
|
|
if (chip_info->tiletype_names[tn.type_idx].get() == type)
|
|
return tn.name.get();
|
|
}
|
|
NPNR_ASSERT_FALSE_STR("no tile at (" + std::to_string(col) + ", " + std::to_string(row) + ") with type " +
|
|
type);
|
|
}
|
|
|
|
std::string get_tile_by_type_loc(int row, int col, const std::set<std::string> &type) const
|
|
{
|
|
auto &tileloc = chip_info->tile_info[row * chip_info->width + col];
|
|
for (auto &tn : tileloc.tile_names) {
|
|
if (type.count(chip_info->tiletype_names[tn.type_idx].get()))
|
|
return tn.name.get();
|
|
}
|
|
NPNR_ASSERT_FALSE_STR("no tile at (" + std::to_string(col) + ", " + std::to_string(row) + ") with type in set");
|
|
}
|
|
|
|
std::string get_tile_by_type(std::string type) const
|
|
{
|
|
for (int i = 0; i < chip_info->height * chip_info->width; i++) {
|
|
auto &tileloc = chip_info->tile_info[i];
|
|
for (auto &tn : tileloc.tile_names)
|
|
if (chip_info->tiletype_names[tn.type_idx].get() == type)
|
|
return tn.name.get();
|
|
}
|
|
NPNR_ASSERT_FALSE_STR("no tile with type " + type);
|
|
}
|
|
|
|
bool is_spine_row(int row) const;
|
|
// Apply LPF constraints to the context
|
|
bool apply_lpf(std::string filename, std::istream &in);
|
|
|
|
// Special case for delay estimates due to its physical location
|
|
// being far from the logical location of its primitive
|
|
WireId gsrclk_wire;
|
|
// Improves directivity of routing to DSP inputs, avoids issues
|
|
// with different routes to the same physical reset wire causing
|
|
// conflicts and slow routing
|
|
dict<WireId, std::pair<int, int>> wire_loc_overrides;
|
|
void setup_wire_locations();
|
|
|
|
mutable dict<DelayKey, std::pair<bool, DelayQuad>> celldelay_cache;
|
|
|
|
// Global clock routing
|
|
void route_globals();
|
|
|
|
static const std::string defaultPlacer;
|
|
static const std::vector<std::string> availablePlacers;
|
|
static const std::string defaultRouter;
|
|
static const std::vector<std::string> availableRouters;
|
|
|
|
mutable std::vector<TileStatus> tile_status;
|
|
|
|
// -------------------------------------------------
|
|
};
|
|
|
|
NEXTPNR_NAMESPACE_END
|
|
|
|
#endif /* MACHXO2_ARCH_H */
|