aboutsummaryrefslogtreecommitdiffstats
path: root/fpga_interchange/arch_place_constr.cc
blob: 3af48656b571f099501948dc2ce48a2f75fd457d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2021  Symbiflow Authors
 *
 *
 *  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.
 *
 */

#include "log.h"
#include "nextpnr.h"
#include "util.h"

NEXTPNR_NAMESPACE_BEGIN

void Arch::place_constraints()
{
    std::vector<std::pair<IdString, BelId>> constrained_cells;
    for (auto &cell_pair : cells) {
        CellInfo *cell = cell_pair.second.get();
        auto loc_constr = cell->attrs.find(id("LOC"));
        auto bel_constr = cell->attrs.find(id("BEL"));

        if (loc_constr == cell->attrs.end() || bel_constr == cell->attrs.end())
            continue;

        IdString loc_name = id(loc_constr->second.as_string());
        IdString bel_name = id(bel_constr->second.as_string());

        BelId bel;
        for (size_t i = 0; i < chip_info->tiles.size(); ++i) {
            const auto &tile = chip_info->tiles[i];
            bool site_found = false;
            for (size_t j = 0; j < tile.sites.size(); ++j) {
                auto &site_data = chip_info->sites[tile.sites[j]];
                if (loc_name == id(site_data.site_name.get())) {
                    site_found = true;
                    break;
                }
            }

            if (!site_found)
                continue;

            const auto &tile_type = chip_info->tile_types[tile.type];
            bool bel_found = false;
            for (size_t j = 0; j < tile_type.bel_data.size(); ++j) {
                const BelInfoPOD &bel_data = tile_type.bel_data[j];
                if (bel_name == IdString(bel_data.name)) {
                    bel.tile = i;
                    bel.index = j;
                    bel_found = true;
                    break;
                }
            }

            if (bel_found)
                break;
            else
                log_error("No bel found for user constraint \'%s/%s\' for cell \'%s\'\n", loc_name.c_str(getCtx()),
                          bel_name.c_str(getCtx()), cell->name.c_str(getCtx()));
        }

        if (!isValidBelForCellType(cell->type, bel))
            log_error("Bel \'%s\' is invalid for cell \'%s\' (%s)\n", nameOfBel(bel), cell->name.c_str(getCtx()),
                      cell->type.c_str(getCtx()));

        auto bound_cell = getBoundBelCell(bel);
        if (bound_cell)
            log_error("Cell \'%s\' cannot be bound to bel \'%s\' "
                      "since it is already bound to cell \'%s\'\n",
                      cell->name.c_str(getCtx()), nameOfBel(bel), bound_cell->name.c_str(getCtx()));

        bindBel(bel, cell, STRENGTH_USER);

        cell->attrs.erase(id("BEL"));
        constrained_cells.emplace_back(cell->name, bel);
    }

    if (constrained_cells.empty())
        return;

    log_info("Cell placed via user constraints:\n");
    for (auto cell_bel : constrained_cells) {
        IdString cell_name = cell_bel.first;
        BelId bel = cell_bel.second;

        if (!isBelLocationValid(bel))
            log_error("  - Bel \'%s\' is not valid for cell \'%s\'\n", nameOfBel(bel), cell_name.c_str(getCtx()));

        log_info("  - %s placed at %s\n", cell_name.c_str(getCtx()), nameOfBel(cell_bel.second));
    }
}

NEXTPNR_NAMESPACE_END
> for (int &sidx : idx_sel) sidx++; } } // If LUT would be too large, bail. if (GetSize(result.second) > max_lut_size) return false; // Okay, we're doing it — compute the LUT mask. result.first = Const(0, 1 << GetSize(result.second)); for (int i = 0; i < GetSize(result.first); i++) { int sel_lut_idx = 0; for (int j = 0; j < GetSize(select.second); j++) if (i & 1 << idx_sel[j]) sel_lut_idx |= 1 << j; bool select_val = (select.first.bits[sel_lut_idx] == State::S1); bool new_bit; if (select_val ^ select_inv) { // Use alt_data. if (alt_data.wire) new_bit = (i & 1 << idx_alt) != 0; else new_bit = alt_data.data == State::S1; } else { // Use original LUT. int lut_idx = i >> idx_data & ((1 << GetSize(data.second)) - 1); new_bit = data.first.bits[lut_idx] == State::S1; } result.first.bits[i] = new_bit ? State::S1 : State::S0; } return true; } struct XilinxDffOptPass : public Pass { XilinxDffOptPass() : Pass("xilinx_dffopt", "Xilinx: optimize FF control signal usage") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" xilinx_dffopt [options] [selection]\n"); log("\n"); log("Converts hardware clock enable and set/reset signals on FFs to emulation\n"); log("using LUTs, if doing so would improve area. Operates on post-techmap Xilinx\n"); log("cells (LUT*, FD*).\n"); log("\n"); log(" -lut4\n"); log(" Assume a LUT4-based device (instead of a LUT6-based device).\n"); log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing XILINX_DFFOPT pass (optimize FF control signal usage).\n"); size_t argidx; int max_lut_size = 6; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-lut4") { max_lut_size = 4; continue; } break; } extra_args(args, argidx, design); for (auto module : design->selected_modules()) { log("Optimizing FFs in %s.\n", log_id(module)); SigMap sigmap(module); dict<SigBit, pair<LutData, Cell *>> bit_to_lut; dict<SigBit, int> bit_uses; // Gather LUTs. for (auto cell : module->selected_cells()) { for (auto port : cell->connections()) for (auto bit : port.second) bit_uses[sigmap(bit)]++; if (cell->get_bool_attribute(ID::keep)) continue; if (cell->type == ID(INV)) { SigBit sigout = sigmap(cell->getPort(ID::O)); SigBit sigin = sigmap(cell->getPort(ID::I)); bit_to_lut[sigout] = make_pair(LutData(Const(1, 2), {sigin}), cell); } else if (cell->type.in(ID(LUT1), ID(LUT2), ID(LUT3), ID(LUT4), ID(LUT5), ID(LUT6))) { SigBit sigout = sigmap(cell->getPort(ID::O)); const Const &init = cell->getParam(ID::INIT); std::vector<SigBit> sigin; sigin.push_back(sigmap(cell->getPort(ID(I0)))); if (cell->type == ID(LUT1)) goto lut_sigin_done; sigin.push_back(sigmap(cell->getPort(ID(I1)))); if (cell->type == ID(LUT2)) goto lut_sigin_done; sigin.push_back(sigmap(cell->getPort(ID(I2)))); if (cell->type == ID(LUT3)) goto lut_sigin_done; sigin.push_back(sigmap(cell->getPort(ID(I3)))); if (cell->type == ID(LUT4)) goto lut_sigin_done; sigin.push_back(sigmap(cell->getPort(ID(I4)))); if (cell->type == ID(LUT5)) goto lut_sigin_done; sigin.push_back(sigmap(cell->getPort(ID(I5)))); lut_sigin_done: bit_to_lut[sigout] = make_pair(LutData(init, sigin), cell); } } for (auto wire : module->wires()) if (wire->port_output || wire->port_input) for (int i = 0; i < GetSize(wire); i++) bit_uses[sigmap(SigBit(wire, i))]++; // Iterate through FFs. for (auto cell : module->selected_cells()) { bool has_s = false, has_r = false; if (cell->type.in(ID(FDCE), ID(FDPE), ID(FDCPE), ID(FDCE_1), ID(FDPE_1), ID(FDCPE_1))) { // Async reset. } else if (cell->type.in(ID(FDRE), ID(FDRE_1))) { has_r = true; } else if (cell->type.in(ID(FDSE), ID(FDSE_1))) { has_s = true; } else if (cell->type.in(ID(FDRSE), ID(FDRSE_1))) { has_r = true; has_s = true; } else { // Not a FF. continue; } if (cell->get_bool_attribute(ID::keep)) continue; // Don't bother if D has more than one use. SigBit sig_D = sigmap(cell->getPort(ID::D)); if (bit_uses[sig_D] > 2) continue; // Find the D LUT. auto it_D = bit_to_lut.find(sig_D); if (it_D == bit_to_lut.end()) continue; LutData lut_d = it_D->second.first; Cell *cell_d = it_D->second.second; if (cell->getParam(ID(IS_D_INVERTED)).as_bool()) { // Flip all bits in the LUT. for (int i = 0; i < GetSize(lut_d.first); i++) lut_d.first.bits[i] = (lut_d.first.bits[i] == State::S1) ? State::S0 : State::S1; } LutData lut_d_post_ce; LutData lut_d_post_s; LutData lut_d_post_r; bool worthy_post_ce = false; bool worthy_post_s = false; bool worthy_post_r = false; // First, unmap CE. SigBit sig_Q = sigmap(cell->getPort(ID::Q)); SigBit sig_CE = sigmap(cell->getPort(ID(CE))); LutData lut_ce = LutData(Const(2, 2), {sig_CE}); auto it_CE = bit_to_lut.find(sig_CE); if (it_CE != bit_to_lut.end()) lut_ce = it_CE->second.first; if (sig_CE.wire) { // Merge CE LUT and D LUT into one. If it cannot be done, nothing to do about this FF. if (!merge_lut(lut_d_post_ce, lut_d, lut_ce, true, sig_Q, max_lut_size)) continue; // If this gets rid of a CE LUT, it's worth it. If not, it still may be worth it, if we can remove set/reset as well. if (it_CE != bit_to_lut.end()) worthy_post_ce = true; } else if (sig_CE.data != State::S1) { // Strange. Should not happen in a reasonable flow, so bail. continue; } else { lut_d_post_ce = lut_d; } // Second, unmap S, if any. lut_d_post_s = lut_d_post_ce; if (has_s) { SigBit sig_S = sigmap(cell->getPort(ID::S)); LutData lut_s = LutData(Const(2, 2), {sig_S}); bool inv_s = cell->getParam(ID(IS_S_INVERTED)).as_bool(); auto it_S = bit_to_lut.find(sig_S); if (it_S != bit_to_lut.end()) lut_s = it_S->second.first; if (sig_S.wire) { // Merge S LUT and D LUT into one. If it cannot be done, try to at least merge CE. if (!merge_lut(lut_d_post_s, lut_d_post_ce, lut_s, inv_s, SigBit(State::S1), max_lut_size)) goto unmap; // If this gets rid of an S LUT, it's worth it. if (it_S != bit_to_lut.end()) worthy_post_s = true; } else if (sig_S.data != (inv_s ? State::S1 : State::S0)) { // Strange. Should not happen in a reasonable flow, so bail. continue; } } // Third, unmap R, if any. lut_d_post_r = lut_d_post_s; if (has_r) { SigBit sig_R = sigmap(cell->getPort(ID::R)); LutData lut_r = LutData(Const(2, 2), {sig_R}); bool inv_r = cell->getParam(ID(IS_R_INVERTED)).as_bool(); auto it_R = bit_to_lut.find(sig_R); if (it_R != bit_to_lut.end()) lut_r = it_R->second.first; if (sig_R.wire) { // Merge R LUT and D LUT into one. If it cannot be done, try to at least merge CE/S. if (!merge_lut(lut_d_post_r, lut_d_post_s, lut_r, inv_r, SigBit(State::S0), max_lut_size)) goto unmap; // If this gets rid of an S LUT, it's worth it. if (it_R != bit_to_lut.end()) worthy_post_r = true; } else if (sig_R.data != (inv_r ? State::S1 : State::S0)) { // Strange. Should not happen in a reasonable flow, so bail. continue; } } unmap: LutData final_lut; if (worthy_post_r) { final_lut = lut_d_post_r; } else if (worthy_post_s) { final_lut = lut_d_post_s; } else if (worthy_post_ce) { final_lut = lut_d_post_ce; } else { // Nothing to do here. continue; } std::string ports; if (worthy_post_r) ports += " + R"; if (worthy_post_s) ports += " + S"; if (worthy_post_ce) ports += " + CE"; log(" Merging D%s LUTs for %s/%s (%d -> %d)\n", ports.c_str(), log_id(cell), log_id(sig_Q.wire), GetSize(lut_d.second), GetSize(final_lut.second)); // Okay, we're doing it. Unmap ports. if (worthy_post_r) { cell->unsetParam(ID(IS_R_INVERTED)); cell->setPort(ID::R, Const(0, 1)); } if (has_s && (worthy_post_r || worthy_post_s)) { cell->unsetParam(ID(IS_S_INVERTED)); cell->setPort(ID::S, Const(0, 1)); } cell->setPort(ID(CE), Const(1, 1)); cell->unsetParam(ID(IS_D_INVERTED)); // Create the new LUT. Cell *lut_cell = 0; switch (GetSize(final_lut.second)) { case 1: lut_cell = module->addCell(NEW_ID, ID(LUT1)); break; case 2: lut_cell = module->addCell(NEW_ID, ID(LUT2)); break; case 3: lut_cell = module->addCell(NEW_ID, ID(LUT3)); break; case 4: lut_cell = module->addCell(NEW_ID, ID(LUT4)); break; case 5: lut_cell = module->addCell(NEW_ID, ID(LUT5)); break; case 6: lut_cell = module->addCell(NEW_ID, ID(LUT6)); break; default: log_assert(!"unknown lut size"); } lut_cell->attributes = cell_d->attributes; Wire *lut_out = module->addWire(NEW_ID); lut_cell->setParam(ID::INIT, final_lut.first); cell->setPort(ID::D, lut_out); lut_cell->setPort(ID::O, lut_out); lut_cell->setPort(ID(I0), final_lut.second[0]); if (GetSize(final_lut.second) >= 2) lut_cell->setPort(ID(I1), final_lut.second[1]); if (GetSize(final_lut.second) >= 3) lut_cell->setPort(ID(I2), final_lut.second[2]); if (GetSize(final_lut.second) >= 4) lut_cell->setPort(ID(I3), final_lut.second[3]); if (GetSize(final_lut.second) >= 5) lut_cell->setPort(ID(I4), final_lut.second[4]); if (GetSize(final_lut.second) >= 6) lut_cell->setPort(ID(I5), final_lut.second[5]); } } } } XilinxDffOptPass; PRIVATE_NAMESPACE_END