| /* |
| * yosys -- Yosys Open SYnthesis Suite |
| * |
| * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> |
| * |
| * 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 "kernel/yosys.h" |
| #include "kernel/sigtools.h" |
| |
| USING_YOSYS_NAMESPACE |
| PRIVATE_NAMESPACE_BEGIN |
| |
| struct NlutmapConfig |
| { |
| vector<int> luts; |
| bool assert_mode = false; |
| }; |
| |
| struct NlutmapWorker |
| { |
| const NlutmapConfig &config; |
| pool<Cell*> mapped_cells; |
| Module *module; |
| |
| NlutmapWorker(const NlutmapConfig &config, Module *module) : |
| config(config), module(module) |
| { |
| } |
| |
| RTLIL::Selection get_selection() |
| { |
| RTLIL::Selection sel(false); |
| for (auto cell : module->cells()) |
| if (!mapped_cells.count(cell)) |
| sel.select(module, cell); |
| return sel; |
| } |
| |
| void run_abc(int lut_size) |
| { |
| Pass::call_on_selection(module->design, get_selection(), "lut2mux"); |
| |
| if (lut_size > 0) |
| Pass::call_on_selection(module->design, get_selection(), stringf("abc -lut 1:%d", lut_size)); |
| else |
| Pass::call_on_selection(module->design, get_selection(), "abc"); |
| |
| Pass::call_on_module(module->design, module, "opt_clean"); |
| } |
| |
| void run() |
| { |
| vector<int> available_luts = config.luts; |
| |
| while (GetSize(available_luts) > 1) |
| { |
| int n_luts = available_luts.back(); |
| int lut_size = GetSize(available_luts); |
| available_luts.pop_back(); |
| |
| if (n_luts == 0) |
| continue; |
| |
| run_abc(lut_size); |
| |
| SigMap sigmap(module); |
| dict<Cell*, int> candidate_ratings; |
| dict<SigBit, int> bit_lut_count; |
| |
| for (auto cell : module->cells()) |
| { |
| if (cell->type != ID($lut) || mapped_cells.count(cell)) |
| continue; |
| |
| if (GetSize(cell->getPort(ID::A)) == lut_size || lut_size == 2) |
| candidate_ratings[cell] = 0; |
| |
| for (auto &conn : cell->connections()) |
| for (auto bit : sigmap(conn.second)) |
| bit_lut_count[bit]++; |
| } |
| |
| for (auto &cand : candidate_ratings) |
| { |
| for (auto &conn : cand.first->connections()) |
| for (auto bit : sigmap(conn.second)) |
| cand.second -= bit_lut_count[bit]; |
| } |
| |
| vector<pair<int, IdString>> rated_candidates; |
| |
| for (auto &cand : candidate_ratings) |
| rated_candidates.push_back(pair<int, IdString>(cand.second, cand.first->name)); |
| |
| std::sort(rated_candidates.begin(), rated_candidates.end()); |
| |
| while (n_luts > 0 && !rated_candidates.empty()) { |
| mapped_cells.insert(module->cell(rated_candidates.back().second)); |
| rated_candidates.pop_back(); |
| n_luts--; |
| } |
| |
| if (!available_luts.empty()) |
| available_luts.back() += n_luts; |
| } |
| |
| if (config.assert_mode) { |
| for (auto cell : module->cells()) |
| if (cell->type == ID($lut) && !mapped_cells.count(cell)) |
| log_error("Insufficient number of LUTs to map all logic cells!\n"); |
| } |
| |
| run_abc(0); |
| } |
| }; |
| |
| struct NlutmapPass : public Pass { |
| NlutmapPass() : Pass("nlutmap", "map to LUTs of different sizes") { } |
| void help() YS_OVERRIDE |
| { |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| log("\n"); |
| log(" nlutmap [options] [selection]\n"); |
| log("\n"); |
| log("This pass uses successive calls to 'abc' to map to an architecture. That\n"); |
| log("provides a small number of differently sized LUTs.\n"); |
| log("\n"); |
| log(" -luts N_1,N_2,N_3,...\n"); |
| log(" The number of LUTs with 1, 2, 3, ... inputs that are\n"); |
| log(" available in the target architecture.\n"); |
| log("\n"); |
| log(" -assert\n"); |
| log(" Create an error if not all logic can be mapped\n"); |
| log("\n"); |
| log("Excess logic that does not fit into the specified LUTs is mapped back\n"); |
| log("to generic logic gates ($_AND_, etc.).\n"); |
| log("\n"); |
| } |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE |
| { |
| NlutmapConfig config; |
| |
| log_header(design, "Executing NLUTMAP pass (mapping to constant drivers).\n"); |
| log_push(); |
| |
| size_t argidx; |
| for (argidx = 1; argidx < args.size(); argidx++) |
| { |
| if (args[argidx] == "-luts" && argidx+1 < args.size()) { |
| vector<string> tokens = split_tokens(args[++argidx], ","); |
| config.luts.clear(); |
| for (auto &token : tokens) |
| config.luts.push_back(atoi(token.c_str())); |
| continue; |
| } |
| if (args[argidx] == "-assert") { |
| config.assert_mode = true; |
| continue; |
| } |
| break; |
| } |
| extra_args(args, argidx, design); |
| |
| for (auto module : design->selected_whole_modules_warn()) |
| { |
| NlutmapWorker worker(config, module); |
| worker.run(); |
| } |
| |
| log_pop(); |
| } |
| } NlutmapPass; |
| |
| PRIVATE_NAMESPACE_END |