From f36188f2e18783ea692f8b6132712a0d1da6b6b7 Mon Sep 17 00:00:00 2001 From: gatecat Date: Mon, 13 Dec 2021 16:04:45 +0000 Subject: ecp5: LUT permutation support Signed-off-by: gatecat --- ecp5/bitstream.cc | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) (limited to 'ecp5/bitstream.cc') diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index 338c4f20..de6c711e 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -522,6 +522,55 @@ static void set_pip(Context *ctx, ChipConfig &cc, PipId pip) cc.tiles[tile].add_arc(sink, source); } +static unsigned permute_lut(Context *ctx, CellInfo *cell, pool &used_phys_pins, int k, unsigned orig_init) +{ + std::array, 4> phys_to_log; + const std::array ports{k ? id_A1 : id_A0, k ? id_B1 : id_B0, k ? id_C1 : id_C0, k ? id_D1 : id_D0}; + 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->loc_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->sliceInfo.is_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 parse_config_str(const Property &p, int length) { std::vector word; @@ -787,12 +836,15 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex } BelId bel = ci->bel; if (ci->type == ctx->id("TRELLIS_SLICE")) { + pool used_phys_pins; std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2"); std::string slice = ctx->loc_info(bel)->bel_data[bel.index].name.get(); int lut0_init = int_or_default(ci->params, ctx->id("LUT0_INITVAL")); int lut1_init = int_or_default(ci->params, ctx->id("LUT1_INITVAL")); - cc.tiles[tname].add_word(slice + ".K0.INIT", int_to_bitvector(lut0_init, 16)); - cc.tiles[tname].add_word(slice + ".K1.INIT", int_to_bitvector(lut1_init, 16)); + cc.tiles[tname].add_word(slice + ".K0.INIT", + int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 0, lut0_init), 16)); + cc.tiles[tname].add_word(slice + ".K1.INIT", + int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 1, lut1_init), 16)); cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, ctx->id("MODE"), "LOGIC")); cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED")); cc.tiles[tname].add_enum(slice + ".REG0.SD", intstr_or_default(ci->params, ctx->id("REG0_SD"), "0")); @@ -854,7 +906,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex // Tie unused inputs high for (auto input : {id_A0, id_B0, id_C0, id_D0, id_A1, id_B1, id_C1, id_D1}) { - if (ci->ports.find(input) == ci->ports.end() || ci->ports.at(input).net == nullptr) { + if (!used_phys_pins.count(input)) { cc.tiles[tname].add_enum(slice + "." + input.str(ctx) + "MUX", "1"); } } -- cgit v1.2.3