add missing bind and lutperm

This commit is contained in:
Miodrag Milanovic 2023-03-30 15:24:53 +02:00 committed by myrtle
parent 7f8518d938
commit 62ace58204
5 changed files with 326 additions and 1 deletions

View File

@ -127,6 +127,29 @@ Arch::Arch(ArchArgs args) : args(args)
y_ids.push_back(y_id);
id_to_y[y_id] = i;
}
wire_tile_vecidx.resize(chip_info->num_tiles, -1);
int n_wires = 0;
for (auto e : getWires()) {
if (e.index == 0) {
wire_tile_vecidx.at(e.location.y * chip_info->width + e.location.x) = n_wires;
}
n_wires++;
}
wire2net.resize(n_wires, nullptr);
wire_fanout.resize(n_wires, 0);
pip_tile_vecidx.resize(chip_info->num_tiles, -1);
int n_pips = 0;
for (auto e : getPips()) {
if (e.index == 0) {
pip_tile_vecidx.at(e.location.y * chip_info->width + e.location.x) = n_pips;
}
n_pips++;
}
pip2net.resize(n_pips, nullptr);
lutperm_allowed.resize(chip_info->width * chip_info->height * 4);
}
void Arch::list_devices()
@ -395,6 +418,9 @@ bool Arch::place()
bool Arch::route()
{
std::string router = str_or_default(settings, id_router, defaultRouter);
disable_router_lutperm = getCtx()->setting<bool>("arch.disable_router_lutperm", false);
bool result;
if (router == "router1") {
result = router1(getCtx(), Router1Cfg(getCtx()));
@ -503,4 +529,16 @@ std::vector<std::pair<std::string, std::string>> Arch::get_tiles_at_loc(int row,
return ret;
}
// -----------------------------------------------------------------------
std::vector<std::pair<IdString, std::string>> Arch::getWireAttrs(WireId wire) const
{
std::vector<std::pair<IdString, std::string>> ret;
auto &wi = tile_info(wire)->wire_data[wire.index];
ret.push_back(std::make_pair(id_TILE_WIRE_ID, stringf("%d", wi.tile_wire)));
return ret;
}
NEXTPNR_NAMESPACE_END

View File

@ -66,6 +66,11 @@ NPNR_PACKED_STRUCT(struct PipInfoPOD {
int16_t padding2;
});
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;
@ -383,6 +388,15 @@ struct Arch : BaseArch<ArchRanges>
// inverse of the above for name->object mapping
dict<IdString, int> id_to_x, id_to_y;
enum class LutPermRule
{
NONE,
CARRY,
ALL,
};
std::vector<LutPermRule> lutperm_allowed;
bool disable_router_lutperm = false;
enum LogicBELType
{
BEL_COMB = 0,
@ -415,6 +429,17 @@ struct Arch : BaseArch<ArchRanges>
mutable std::vector<TileStatus> tile_status;
// 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;
// Helpers
template <typename Id> const TileTypePOD *tile_info(Id &id) const
{
@ -470,6 +495,62 @@ struct Arch : BaseArch<ArchRanges>
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());
@ -484,6 +565,27 @@ struct Arch : BaseArch<ArchRanges>
BelRange getBelsByTile(int x, int y) const override;
bool getBelGlobalBuf(BelId bel) const override;
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;
@ -523,6 +625,60 @@ struct Arch : BaseArch<ArchRanges>
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.01); }
WireRange getWires() const override
@ -568,6 +724,78 @@ struct Arch : BaseArch<ArchRanges>
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;
@ -648,6 +876,11 @@ struct Arch : BaseArch<ArchRanges>
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();
}
// Delay
delay_t estimateDelay(WireId src, WireId dst) const override;
delay_t predictDelay(BelId src_bel, IdString src_pin, BelId dst_bel, IdString dst_pin) const override;

View File

@ -404,6 +404,55 @@ static void set_pip(Context *ctx, ChipConfig &cc, PipId pip)
}
}
static unsigned permute_lut(Context *ctx, CellInfo *cell, pool<IdString> &used_phys_pins, unsigned orig_init)
{
std::array<std::vector<unsigned>, 4> phys_to_log;
const std::array<IdString, 4> ports{id_A, id_B, id_C, id_D};
for (unsigned i = 0; i < 4; i++) {
WireId pin_wire = ctx->getBelPinWire(cell->bel, ports[i]);
for (PipId pip : ctx->getPipsUphill(pin_wire)) {
if (!ctx->getBoundPipNet(pip))
continue;
unsigned lp = ctx->tile_info(pip)->pip_data[pip.index].lutperm_flags;
if (!is_lutperm_pip(lp)) { // non-permuting
phys_to_log[i].push_back(i);
} else { // permuting
unsigned from_pin = lutperm_in(lp);
unsigned to_pin = lutperm_out(lp);
NPNR_ASSERT(to_pin == i);
phys_to_log[from_pin].push_back(i);
}
}
}
for (unsigned i = 0; i < 4; i++)
if (!phys_to_log.at(i).empty())
used_phys_pins.insert(ports.at(i));
if (cell->combInfo.flags & ArchCellInfo::COMB_CARRY) {
// Insert dummy entries to ensure we keep the split between the two halves of a CCU2
for (unsigned i = 0; i < 4; i++) {
if (!phys_to_log.at(i).empty())
continue;
for (unsigned j = 2 * (i / 2); j < 2 * ((i / 2) + 1); j++) {
if (!ctx->getBoundWireNet(ctx->getBelPinWire(cell->bel, ports[j])))
phys_to_log.at(i).push_back(j);
}
}
}
unsigned permuted_init = 0;
for (unsigned i = 0; i < 16; i++) {
unsigned log_idx = 0;
for (unsigned j = 0; j < 4; j++) {
if ((i >> j) & 0x1) {
for (auto log_pin : phys_to_log[j])
log_idx |= (1 << log_pin);
}
}
if ((orig_init >> log_idx) & 0x1)
permuted_init |= (1 << i);
}
return permuted_init;
}
static std::vector<bool> int_to_bitvector(int val, int size)
{
std::vector<bool> bv;
@ -526,7 +575,8 @@ void write_bitstream(Context *ctx, std::string text_config_file)
return;
int lut_init = int_or_default(ci->params, id_INITVAL);
cc.tiles[tname].add_enum(slice + ".MODE", mode);
cc.tiles[tname].add_word(slice + ".K" + lc + ".INIT", int_to_bitvector(lut_init, 16));
cc.tiles[tname].add_word(slice + ".K" + lc + ".INIT",
int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, lut_init), 16));
if (mode == "CCU2") {
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_" + lc, str_or_default(ci->params, id_CCU2_INJECT1, "YES"));
} else {

View File

@ -126,6 +126,7 @@ X(NOM_FREQ)
X(VCC)
X(WIRE_TYPE_NONE)
X(TILE_WIRE_ID)
X(machxo2)
X(pack)

View File

@ -51,6 +51,7 @@ po::options_description MachXO2CommandHandler::getArchOptions()
specific.add_options()("list-devices", "list all supported device names");
specific.add_options()("textcfg", po::value<std::string>(), "textual configuration in Trellis format to write");
// specific.add_options()("lpf", po::value<std::vector<std::string>>(), "LPF pin constraint file(s)");
specific.add_options()("disable-router-lutperm", "don't allow the router to permute LUT inputs");
return specific;
}
@ -76,6 +77,8 @@ std::unique_ptr<Context> MachXO2CommandHandler::createContext(dict<std::string,
}
chipArgs.device = vm["device"].as<std::string>();
auto ctx = std::unique_ptr<Context>(new Context(chipArgs));
if (vm.count("disable-router-lutperm"))
ctx->settings[ctx->id("arch.disable_router_lutperm")] = 1;
return ctx;
}