| /* |
| * 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" |
| #include "libparse.h" |
| #include <string.h> |
| #include <errno.h> |
| |
| USING_YOSYS_NAMESPACE |
| PRIVATE_NAMESPACE_BEGIN |
| |
| struct cell_mapping { |
| IdString cell_name; |
| std::map<std::string, char> ports; |
| }; |
| static std::map<RTLIL::IdString, cell_mapping> cell_mappings; |
| |
| static void logmap(IdString dff) |
| { |
| if (cell_mappings.count(dff) == 0) { |
| log(" unmapped dff cell: %s\n", dff.c_str()); |
| } else { |
| log(" %s %s (", cell_mappings[dff].cell_name.c_str(), dff.substr(1).c_str()); |
| bool first = true; |
| for (auto &port : cell_mappings[dff].ports) { |
| char arg[3] = { port.second, 0, 0 }; |
| if ('a' <= arg[0] && arg[0] <= 'z') |
| arg[1] = arg[0] - ('a' - 'A'), arg[0] = '~'; |
| else |
| arg[1] = arg[0], arg[0] = ' '; |
| log("%s.%s(%s)", first ? "" : ", ", port.first.c_str(), arg); |
| first = false; |
| } |
| log(");\n"); |
| } |
| } |
| |
| static void logmap_all() |
| { |
| logmap(ID($_DFF_N_)); |
| logmap(ID($_DFF_P_)); |
| |
| logmap(ID($_DFF_NN0_)); |
| logmap(ID($_DFF_NN1_)); |
| logmap(ID($_DFF_NP0_)); |
| logmap(ID($_DFF_NP1_)); |
| logmap(ID($_DFF_PN0_)); |
| logmap(ID($_DFF_PN1_)); |
| logmap(ID($_DFF_PP0_)); |
| logmap(ID($_DFF_PP1_)); |
| |
| logmap(ID($_DFFSR_NNN_)); |
| logmap(ID($_DFFSR_NNP_)); |
| logmap(ID($_DFFSR_NPN_)); |
| logmap(ID($_DFFSR_NPP_)); |
| logmap(ID($_DFFSR_PNN_)); |
| logmap(ID($_DFFSR_PNP_)); |
| logmap(ID($_DFFSR_PPN_)); |
| logmap(ID($_DFFSR_PPP_)); |
| } |
| |
| static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name, bool &pin_pol) |
| { |
| if (cell == NULL || attr == NULL || attr->value.empty()) |
| return false; |
| |
| std::string value = attr->value; |
| |
| for (size_t pos = value.find_first_of("\" \t()"); pos != std::string::npos; pos = value.find_first_of("\" \t()")) |
| value.erase(pos, 1); |
| |
| if (value[value.size()-1] == '\'') { |
| pin_name = value.substr(0, value.size()-1); |
| pin_pol = false; |
| } else if (value[0] == '!') { |
| pin_name = value.substr(1, value.size()-1); |
| pin_pol = false; |
| } else { |
| pin_name = value; |
| pin_pol = true; |
| } |
| |
| for (auto child : cell->children) |
| if (child->id == "pin" && child->args.size() == 1 && child->args[0] == pin_name) |
| return true; |
| |
| /* If we end up here, the pin specified in the attribute does not exist, which is an error, |
| or, the attribute contains an expression which we do not yet support. |
| For now, we'll simply produce a warning to let the user know something is up. |
| */ |
| if (pin_name.find_first_of("^*|&") == std::string::npos) { |
| log_warning("Malformed liberty file - cannot find pin '%s' in cell '%s' - skipping.\n", pin_name.c_str(), cell->args[0].c_str()); |
| } |
| else { |
| log_warning("Found unsupported expression '%s' in pin attribute of cell '%s' - skipping.\n", pin_name.c_str(), cell->args[0].c_str()); |
| } |
| |
| return false; |
| } |
| |
| static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, bool prepare_mode) |
| { |
| LibertyAst *best_cell = NULL; |
| std::map<std::string, char> best_cell_ports; |
| int best_cell_pins = 0; |
| bool best_cell_noninv = false; |
| double best_cell_area = 0; |
| |
| if (ast->id != "library") |
| log_error("Format error in liberty file.\n"); |
| |
| for (auto cell : ast->children) |
| { |
| if (cell->id != "cell" || cell->args.size() != 1) |
| continue; |
| |
| LibertyAst *dn = cell->find("dont_use"); |
| if (dn != NULL && dn->value == "true") |
| continue; |
| |
| LibertyAst *ff = cell->find("ff"); |
| if (ff == NULL) |
| continue; |
| |
| std::string cell_clk_pin, cell_rst_pin, cell_next_pin; |
| bool cell_clk_pol, cell_rst_pol, cell_next_pol; |
| |
| if (!parse_pin(cell, ff->find("clocked_on"), cell_clk_pin, cell_clk_pol) || cell_clk_pol != clkpol) |
| continue; |
| if (!parse_pin(cell, ff->find("next_state"), cell_next_pin, cell_next_pol)) |
| continue; |
| if (has_reset && rstval == false) { |
| if (!parse_pin(cell, ff->find("clear"), cell_rst_pin, cell_rst_pol) || cell_rst_pol != rstpol) |
| continue; |
| } |
| if (has_reset && rstval == true) { |
| if (!parse_pin(cell, ff->find("preset"), cell_rst_pin, cell_rst_pol) || cell_rst_pol != rstpol) |
| continue; |
| } |
| |
| std::map<std::string, char> this_cell_ports; |
| this_cell_ports[cell_clk_pin] = 'C'; |
| if (has_reset) |
| this_cell_ports[cell_rst_pin] = 'R'; |
| this_cell_ports[cell_next_pin] = 'D'; |
| |
| double area = 0; |
| LibertyAst *ar = cell->find("area"); |
| if (ar != NULL && !ar->value.empty()) |
| area = atof(ar->value.c_str()); |
| |
| int num_pins = 0; |
| bool found_output = false; |
| bool found_noninv_output = false; |
| for (auto pin : cell->children) |
| { |
| if (pin->id != "pin" || pin->args.size() != 1) |
| continue; |
| |
| LibertyAst *dir = pin->find("direction"); |
| if (dir == NULL || dir->value == "internal") |
| continue; |
| num_pins++; |
| |
| if (dir->value == "input" && this_cell_ports.count(pin->args[0]) == 0) |
| goto continue_cell_loop; |
| |
| LibertyAst *func = pin->find("function"); |
| if (dir->value == "output" && func != NULL) { |
| std::string value = func->value; |
| for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t")) |
| value.erase(pos, 1); |
| if (value == ff->args[0]) { |
| this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q'; |
| if (cell_next_pol) |
| found_noninv_output = true; |
| found_output = true; |
| } else |
| if (value == ff->args[1]) { |
| this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q'; |
| if (!cell_next_pol) |
| found_noninv_output = true; |
| found_output = true; |
| } |
| } |
| |
| if (this_cell_ports.count(pin->args[0]) == 0) |
| this_cell_ports[pin->args[0]] = 0; |
| } |
| |
| if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output)))) |
| continue; |
| |
| if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area) |
| continue; |
| |
| best_cell = cell; |
| best_cell_pins = num_pins; |
| best_cell_area = area; |
| best_cell_noninv = found_noninv_output; |
| best_cell_ports.swap(this_cell_ports); |
| continue_cell_loop:; |
| } |
| |
| if (best_cell != NULL) { |
| log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n", |
| best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str()); |
| if (prepare_mode) { |
| cell_mappings[cell_type].cell_name = cell_type; |
| cell_mappings[cell_type].ports["C"] = 'C'; |
| if (has_reset) |
| cell_mappings[cell_type].ports["R"] = 'R'; |
| cell_mappings[cell_type].ports["D"] = 'D'; |
| cell_mappings[cell_type].ports["Q"] = 'Q'; |
| } else { |
| cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]); |
| cell_mappings[cell_type].ports = best_cell_ports; |
| } |
| } |
| } |
| |
| static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol, bool prepare_mode) |
| { |
| LibertyAst *best_cell = NULL; |
| std::map<std::string, char> best_cell_ports; |
| int best_cell_pins = 0; |
| bool best_cell_noninv = false; |
| double best_cell_area = 0; |
| |
| if (ast->id != "library") |
| log_error("Format error in liberty file.\n"); |
| |
| for (auto cell : ast->children) |
| { |
| if (cell->id != "cell" || cell->args.size() != 1) |
| continue; |
| |
| LibertyAst *dn = cell->find("dont_use"); |
| if (dn != NULL && dn->value == "true") |
| continue; |
| |
| LibertyAst *ff = cell->find("ff"); |
| if (ff == NULL) |
| continue; |
| |
| std::string cell_clk_pin, cell_set_pin, cell_clr_pin, cell_next_pin; |
| bool cell_clk_pol, cell_set_pol, cell_clr_pol, cell_next_pol; |
| |
| if (!parse_pin(cell, ff->find("clocked_on"), cell_clk_pin, cell_clk_pol) || cell_clk_pol != clkpol) |
| continue; |
| if (!parse_pin(cell, ff->find("next_state"), cell_next_pin, cell_next_pol)) |
| continue; |
| if (!parse_pin(cell, ff->find("preset"), cell_set_pin, cell_set_pol) || cell_set_pol != setpol) |
| continue; |
| if (!parse_pin(cell, ff->find("clear"), cell_clr_pin, cell_clr_pol) || cell_clr_pol != clrpol) |
| continue; |
| |
| std::map<std::string, char> this_cell_ports; |
| this_cell_ports[cell_clk_pin] = 'C'; |
| this_cell_ports[cell_set_pin] = 'S'; |
| this_cell_ports[cell_clr_pin] = 'R'; |
| this_cell_ports[cell_next_pin] = 'D'; |
| |
| double area = 0; |
| LibertyAst *ar = cell->find("area"); |
| if (ar != NULL && !ar->value.empty()) |
| area = atof(ar->value.c_str()); |
| |
| int num_pins = 0; |
| bool found_output = false; |
| bool found_noninv_output = false; |
| for (auto pin : cell->children) |
| { |
| if (pin->id != "pin" || pin->args.size() != 1) |
| continue; |
| |
| LibertyAst *dir = pin->find("direction"); |
| if (dir == NULL || dir->value == "internal") |
| continue; |
| num_pins++; |
| |
| if (dir->value == "input" && this_cell_ports.count(pin->args[0]) == 0) |
| goto continue_cell_loop; |
| |
| LibertyAst *func = pin->find("function"); |
| if (dir->value == "output" && func != NULL) { |
| std::string value = func->value; |
| for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t")) |
| value.erase(pos, 1); |
| if (value == ff->args[0]) { |
| this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q'; |
| if (cell_next_pol) |
| found_noninv_output = true; |
| found_output = true; |
| } else |
| if (value == ff->args[1]) { |
| this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q'; |
| if (!cell_next_pol) |
| found_noninv_output = true; |
| found_output = true; |
| } |
| } |
| |
| if (this_cell_ports.count(pin->args[0]) == 0) |
| this_cell_ports[pin->args[0]] = 0; |
| } |
| |
| if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output)))) |
| continue; |
| |
| if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area) |
| continue; |
| |
| best_cell = cell; |
| best_cell_pins = num_pins; |
| best_cell_area = area; |
| best_cell_noninv = found_noninv_output; |
| best_cell_ports.swap(this_cell_ports); |
| continue_cell_loop:; |
| } |
| |
| if (best_cell != NULL) { |
| log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n", |
| best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str()); |
| if (prepare_mode) { |
| cell_mappings[cell_type].cell_name = cell_type; |
| cell_mappings[cell_type].ports["C"] = 'C'; |
| cell_mappings[cell_type].ports["S"] = 'S'; |
| cell_mappings[cell_type].ports["R"] = 'R'; |
| cell_mappings[cell_type].ports["D"] = 'D'; |
| cell_mappings[cell_type].ports["Q"] = 'Q'; |
| } else { |
| cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]); |
| cell_mappings[cell_type].ports = best_cell_ports; |
| } |
| } |
| } |
| |
| static bool expand_cellmap_worker(std::string from, std::string to, std::string inv) |
| { |
| if (cell_mappings.count(to) > 0) |
| return false; |
| |
| log(" create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str()); |
| cell_mappings[to].cell_name = cell_mappings[from].cell_name; |
| cell_mappings[to].ports = cell_mappings[from].ports; |
| |
| for (auto &it : cell_mappings[to].ports) { |
| char cmp_ch = it.second; |
| if ('a' <= cmp_ch && cmp_ch <= 'z') |
| cmp_ch -= 'a' - 'A'; |
| if (inv.find(cmp_ch) == std::string::npos) |
| continue; |
| if ('a' <= it.second && it.second <= 'z') |
| it.second -= 'a' - 'A'; |
| else if ('A' <= it.second && it.second <= 'Z') |
| it.second += 'a' - 'A'; |
| } |
| return true; |
| } |
| |
| static bool expand_cellmap(std::string pattern, std::string inv) |
| { |
| std::vector<std::pair<std::string, std::string>> from_to_list; |
| bool return_status = false; |
| |
| for (auto &it : cell_mappings) { |
| std::string from = it.first.str(), to = it.first.str(); |
| if (from.size() != pattern.size()) |
| continue; |
| for (size_t i = 0; i < from.size(); i++) { |
| if (pattern[i] == '*') { |
| to[i] = from[i] == 'P' ? 'N' : |
| from[i] == 'N' ? 'P' : |
| from[i] == '1' ? '0' : |
| from[i] == '0' ? '1' : '*'; |
| } else |
| if (pattern[i] != '?' && pattern[i] != from[i]) |
| goto pattern_failed; |
| } |
| from_to_list.push_back(std::pair<std::string, std::string>(from, to)); |
| pattern_failed:; |
| } |
| |
| for (auto &it : from_to_list) |
| return_status = return_status || expand_cellmap_worker(it.first, it.second, inv); |
| return return_status; |
| } |
| |
| static void map_sr_to_arst(IdString from, IdString to) |
| { |
| if (!cell_mappings.count(from) || cell_mappings.count(to) > 0) |
| return; |
| |
| char from_clk_pol YS_ATTRIBUTE(unused) = from[8]; |
| char from_set_pol = from[9]; |
| char from_clr_pol = from[10]; |
| char to_clk_pol YS_ATTRIBUTE(unused) = to[6]; |
| char to_rst_pol YS_ATTRIBUTE(unused) = to[7]; |
| char to_rst_val = to[8]; |
| |
| log_assert(from_clk_pol == to_clk_pol); |
| log_assert(to_rst_pol == from_set_pol && to_rst_pol == from_clr_pol); |
| |
| log(" create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str()); |
| cell_mappings[to].cell_name = cell_mappings[from].cell_name; |
| cell_mappings[to].ports = cell_mappings[from].ports; |
| |
| for (auto &it : cell_mappings[to].ports) |
| { |
| bool is_set_pin = it.second == 'S' || it.second == 's'; |
| bool is_clr_pin = it.second == 'R' || it.second == 'r'; |
| |
| if (!is_set_pin && !is_clr_pin) |
| continue; |
| |
| if ((to_rst_val == '0' && is_set_pin) || (to_rst_val == '1' && is_clr_pin)) |
| { |
| // this is the unused set/clr pin -- deactivate it |
| if (is_set_pin) |
| it.second = (from_set_pol == 'P') == (it.second == 'S') ? '0' : '1'; |
| else |
| it.second = (from_clr_pol == 'P') == (it.second == 'R') ? '0' : '1'; |
| } |
| else |
| { |
| // this is the used set/clr pin -- rename it to 'reset' |
| if (it.second == 'S') |
| it.second = 'R'; |
| if (it.second == 's') |
| it.second = 'r'; |
| } |
| } |
| } |
| |
| static void map_adff_to_dff(IdString from, IdString to) |
| { |
| if (!cell_mappings.count(from) || cell_mappings.count(to) > 0) |
| return; |
| |
| char from_clk_pol YS_ATTRIBUTE(unused) = from[6]; |
| char from_rst_pol = from[7]; |
| char to_clk_pol YS_ATTRIBUTE(unused) = to[6]; |
| |
| log_assert(from_clk_pol == to_clk_pol); |
| |
| log(" create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str()); |
| cell_mappings[to].cell_name = cell_mappings[from].cell_name; |
| cell_mappings[to].ports = cell_mappings[from].ports; |
| |
| for (auto &it : cell_mappings[to].ports) { |
| if (it.second == 'S' || it.second == 'R') |
| it.second = from_rst_pol == 'P' ? '0' : '1'; |
| if (it.second == 's' || it.second == 'r') |
| it.second = from_rst_pol == 'P' ? '1' : '0'; |
| } |
| } |
| |
| static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare_mode) |
| { |
| log("Mapping DFF cells in module `%s':\n", module->name.c_str()); |
| |
| dict<SigBit, pool<Cell*>> notmap; |
| SigMap sigmap(module); |
| |
| std::vector<RTLIL::Cell*> cell_list; |
| for (auto &it : module->cells_) { |
| if (design->selected(module, it.second) && cell_mappings.count(it.second->type) > 0) |
| cell_list.push_back(it.second); |
| if (it.second->type == ID($_NOT_)) |
| notmap[sigmap(it.second->getPort(ID::A))].insert(it.second); |
| } |
| |
| std::map<std::string, int> stats; |
| for (auto cell : cell_list) |
| { |
| auto cell_type = cell->type; |
| auto cell_name = cell->name; |
| auto cell_connections = cell->connections(); |
| std::string src = cell->get_src_attribute(); |
| |
| module->remove(cell); |
| |
| cell_mapping &cm = cell_mappings[cell_type]; |
| RTLIL::Cell *new_cell = module->addCell(cell_name, prepare_mode ? cm.cell_name : cm.cell_name); |
| |
| new_cell->set_src_attribute(src); |
| |
| bool has_q = false, has_qn = false; |
| for (auto &port : cm.ports) { |
| if (port.second == 'Q') has_q = true; |
| if (port.second == 'q') has_qn = true; |
| } |
| |
| for (auto &port : cm.ports) { |
| RTLIL::SigSpec sig; |
| if ('A' <= port.second && port.second <= 'Z') { |
| sig = cell_connections[std::string("\\") + port.second]; |
| } else |
| if (port.second == 'q') { |
| RTLIL::SigSpec old_sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))]; |
| sig = module->addWire(NEW_ID, GetSize(old_sig)); |
| if (has_q && has_qn) { |
| for (auto &it : notmap[sigmap(old_sig)]) { |
| module->connect(it->getPort(ID::Y), sig); |
| it->setPort(ID::Y, module->addWire(NEW_ID, GetSize(old_sig))); |
| } |
| } else { |
| module->addNotGate(NEW_ID, sig, old_sig); |
| } |
| } else |
| if ('a' <= port.second && port.second <= 'z') { |
| sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))]; |
| sig = module->NotGate(NEW_ID, sig); |
| } else |
| if (port.second == '0' || port.second == '1') { |
| sig = RTLIL::SigSpec(port.second == '0' ? 0 : 1, 1); |
| } else |
| if (port.second == 0) { |
| sig = module->addWire(NEW_ID); |
| } else |
| log_abort(); |
| new_cell->setPort("\\" + port.first, sig); |
| } |
| |
| stats[stringf(" mapped %%d %s cells to %s cells.\n", cell_type.c_str(), new_cell->type.c_str())]++; |
| } |
| |
| for (auto &stat: stats) |
| log(stat.first.c_str(), stat.second); |
| } |
| |
| struct DfflibmapPass : public Pass { |
| DfflibmapPass() : Pass("dfflibmap", "technology mapping of flip-flops") { } |
| void help() YS_OVERRIDE |
| { |
| log("\n"); |
| log(" dfflibmap [-prepare] -liberty <file> [selection]\n"); |
| log("\n"); |
| log("Map internal flip-flop cells to the flip-flop cells in the technology\n"); |
| log("library specified in the given liberty file.\n"); |
| log("\n"); |
| log("This pass may add inverters as needed. Therefore it is recommended to\n"); |
| log("first run this pass and then map the logic paths to the target technology.\n"); |
| log("\n"); |
| log("When called with -prepare, this command will convert the internal FF cells\n"); |
| log("to the internal cell types that best match the cells found in the given\n"); |
| log("liberty file.\n"); |
| log("\n"); |
| } |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE |
| { |
| log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); |
| |
| std::string liberty_file; |
| bool prepare_mode = false; |
| |
| size_t argidx; |
| for (argidx = 1; argidx < args.size(); argidx++) |
| { |
| std::string arg = args[argidx]; |
| if (arg == "-liberty" && argidx+1 < args.size()) { |
| liberty_file = args[++argidx]; |
| rewrite_filename(liberty_file); |
| continue; |
| } |
| if (arg == "-prepare") { |
| prepare_mode = true; |
| continue; |
| } |
| break; |
| } |
| extra_args(args, argidx, design); |
| |
| if (liberty_file.empty()) |
| log_cmd_error("Missing `-liberty liberty_file' option!\n"); |
| |
| std::ifstream f; |
| f.open(liberty_file.c_str()); |
| if (f.fail()) |
| log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno)); |
| LibertyParser libparser(f); |
| f.close(); |
| |
| find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false, prepare_mode); |
| find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false, prepare_mode); |
| |
| find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false, prepare_mode); |
| find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true, prepare_mode); |
| find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false, prepare_mode); |
| find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true, prepare_mode); |
| find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false, prepare_mode); |
| find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true, prepare_mode); |
| find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false, prepare_mode); |
| find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true, prepare_mode); |
| |
| find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false, prepare_mode); |
| find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true, prepare_mode); |
| find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false, prepare_mode); |
| find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true, prepare_mode); |
| find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false, prepare_mode); |
| find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true, prepare_mode); |
| find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false, prepare_mode); |
| find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true, prepare_mode); |
| |
| // try to implement as many cells as possible just by inverting |
| // the SET and RESET pins. If necessary, implement cell types |
| // by inverting both D and Q. Only invert clock pins if there |
| // is no other way of implementing the cell. |
| while (1) |
| { |
| if (expand_cellmap("$_DFF_?*?_", "R") || |
| expand_cellmap("$_DFFSR_?*?_", "S") || |
| expand_cellmap("$_DFFSR_??*_", "R")) |
| continue; |
| |
| if (expand_cellmap("$_DFF_??*_", "DQ")) |
| continue; |
| |
| if (expand_cellmap("$_DFF_*_", "C") || |
| expand_cellmap("$_DFF_*??_", "C") || |
| expand_cellmap("$_DFFSR_*??_", "C")) |
| continue; |
| |
| break; |
| } |
| |
| map_sr_to_arst(ID($_DFFSR_NNN_), ID($_DFF_NN0_)); |
| map_sr_to_arst(ID($_DFFSR_NNN_), ID($_DFF_NN1_)); |
| map_sr_to_arst(ID($_DFFSR_NPP_), ID($_DFF_NP0_)); |
| map_sr_to_arst(ID($_DFFSR_NPP_), ID($_DFF_NP1_)); |
| map_sr_to_arst(ID($_DFFSR_PNN_), ID($_DFF_PN0_)); |
| map_sr_to_arst(ID($_DFFSR_PNN_), ID($_DFF_PN1_)); |
| map_sr_to_arst(ID($_DFFSR_PPP_), ID($_DFF_PP0_)); |
| map_sr_to_arst(ID($_DFFSR_PPP_), ID($_DFF_PP1_)); |
| |
| map_adff_to_dff(ID($_DFF_NN0_), ID($_DFF_N_)); |
| map_adff_to_dff(ID($_DFF_NN1_), ID($_DFF_N_)); |
| map_adff_to_dff(ID($_DFF_NP0_), ID($_DFF_N_)); |
| map_adff_to_dff(ID($_DFF_NP1_), ID($_DFF_N_)); |
| map_adff_to_dff(ID($_DFF_PN0_), ID($_DFF_P_)); |
| map_adff_to_dff(ID($_DFF_PN1_), ID($_DFF_P_)); |
| map_adff_to_dff(ID($_DFF_PP0_), ID($_DFF_P_)); |
| map_adff_to_dff(ID($_DFF_PP1_), ID($_DFF_P_)); |
| |
| log(" final dff cell mappings:\n"); |
| logmap_all(); |
| |
| for (auto &it : design->modules_) |
| if (design->selected(it.second) && !it.second->get_blackbox_attribute()) |
| dfflibmap(design, it.second, prepare_mode); |
| |
| cell_mappings.clear(); |
| } |
| } DfflibmapPass; |
| |
| PRIVATE_NAMESPACE_END |