| /* | |
| * 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 |