diff --git a/machxo2/arch.cc b/machxo2/arch.cc index 5b6f6cae..9e638cbf 100644 --- a/machxo2/arch.cc +++ b/machxo2/arch.cc @@ -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("arch.disable_router_lutperm", false); + bool result; if (router == "router1") { result = router1(getCtx(), Router1Cfg(getCtx())); @@ -503,4 +529,16 @@ std::vector> Arch::get_tiles_at_loc(int row, return ret; } +// ----------------------------------------------------------------------- + +std::vector> Arch::getWireAttrs(WireId wire) const +{ + std::vector> 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 diff --git a/machxo2/arch.h b/machxo2/arch.h index ec5dd831..2be987b7 100644 --- a/machxo2/arch.h +++ b/machxo2/arch.h @@ -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 // inverse of the above for name->object mapping dict id_to_x, id_to_y; + enum class LutPermRule + { + NONE, + CARRY, + ALL, + }; + std::vector lutperm_allowed; + bool disable_router_lutperm = false; + enum LogicBELType { BEL_COMB = 0, @@ -415,6 +429,17 @@ struct Arch : BaseArch mutable std::vector tile_status; + // faster replacements for base_pip2net, base_wire2net + // indexed by get_pip_vecidx() + std::vector pip2net; + // indexed by get_wire_vecidx() + std::vector wire2net; + std::vector 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 pip_tile_vecidx; + std::vector wire_tile_vecidx; + // Helpers template const TileTypePOD *tile_info(Id &id) const { @@ -470,6 +495,62 @@ struct Arch : BaseArch 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 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 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> 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 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 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; diff --git a/machxo2/bitstream.cc b/machxo2/bitstream.cc index 57eb87da..529c5cc9 100644 --- a/machxo2/bitstream.cc +++ b/machxo2/bitstream.cc @@ -404,6 +404,55 @@ static void set_pip(Context *ctx, ChipConfig &cc, PipId pip) } } +static unsigned permute_lut(Context *ctx, CellInfo *cell, pool &used_phys_pins, unsigned orig_init) +{ + std::array, 4> phys_to_log; + const std::array 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 int_to_bitvector(int val, int size) { std::vector 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 { diff --git a/machxo2/constids.inc b/machxo2/constids.inc index 0f5c2e10..adb7254d 100644 --- a/machxo2/constids.inc +++ b/machxo2/constids.inc @@ -126,6 +126,7 @@ X(NOM_FREQ) X(VCC) X(WIRE_TYPE_NONE) +X(TILE_WIRE_ID) X(machxo2) X(pack) diff --git a/machxo2/main.cc b/machxo2/main.cc index 283bc59c..f21f8c3b 100644 --- a/machxo2/main.cc +++ b/machxo2/main.cc @@ -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(), "textual configuration in Trellis format to write"); // specific.add_options()("lpf", po::value>(), "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 MachXO2CommandHandler::createContext(dict(); auto ctx = std::unique_ptr(new Context(chipArgs)); + if (vm.count("disable-router-lutperm")) + ctx->settings[ctx->id("arch.disable_router_lutperm")] = 1; return ctx; }