| /* | 
 |  *  yosys -- Yosys Open SYnthesis Suite | 
 |  * | 
 |  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | 
 |  *  Copyright (C) 2020  The 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 "pcf_parser.hh" | 
 | #include "pinmap_parser.hh" | 
 |  | 
 | #include "kernel/register.h" | 
 | #include "kernel/rtlil.h" | 
 |  | 
 | #include <regex> | 
 | #include <sstream> | 
 |  | 
 | #ifndef YS_OVERRIDE | 
 | #define YS_OVERRIDE override | 
 | #endif | 
 |  | 
 | USING_YOSYS_NAMESPACE | 
 | PRIVATE_NAMESPACE_BEGIN | 
 |  | 
 | struct QuicklogicIob : public Pass { | 
 |  | 
 |     struct IoCellType { | 
 |         std::string type;                        // Cell type | 
 |         std::string port;                        // Name of the port that goes to a pad | 
 |         std::vector<std::string> preferredTypes; // A list of preferred IO cell types | 
 |  | 
 |         IoCellType(const std::string &_type, const std::string &_port, const std::vector<std::string> _preferredTypes = std::vector<std::string>()) | 
 |             : type(_type), port(_port), preferredTypes(_preferredTypes) | 
 |         { | 
 |         } | 
 |     }; | 
 |  | 
 |     QuicklogicIob() : Pass("quicklogic_iob", "Map IO buffers to cells that correspond to their assigned locations") {} | 
 |  | 
 |     void help() YS_OVERRIDE | 
 |     { | 
 |         log("\n"); | 
 |         log("    quicklogic_iob <PCF file> <pinmap file> [<io cell specs>]"); | 
 |         log("\n"); | 
 |         log("This command assigns certain parameters of the specified IO cell types\n"); | 
 |         log("basing on the placement constraints and the pin map of the target device\n"); | 
 |         log("\n"); | 
 |         log("Each affected IO cell is assigned the followin parameters:\n"); | 
 |         log(" - IO_PAD  = \"<IO pad name>\"\n"); | 
 |         log(" - IO_LOC  = \"<IO cell location>\"\n"); | 
 |         log(" - IO_CELL = \"<IO cell type>\"\n"); | 
 |         log("\n"); | 
 |         log("Parameters:\n"); | 
 |         log("\n"); | 
 |         log("    - <PCF file>\n"); | 
 |         log("        Path to a PCF file with IO constraints for the design\n"); | 
 |         log("\n"); | 
 |         log("    - <pinmap file>\n"); | 
 |         log("        Path to a pinmap CSV file with package pin map\n"); | 
 |         log("\n"); | 
 |         log("    - <io cell specs> (optional)\n"); | 
 |         log("        A space-separated list of <io cell type>:<port> or of\n"); | 
 |         log("        <io cell type>:<port>:<preferred type 1>,<preferred type 2>...\n"); | 
 |         log("        Each entry defines a type of IO cell to be affected an its port\n"); | 
 |         log("        name that should connect to the top-level port of the design.\n"); | 
 |         log("\n"); | 
 |         log("        The third argument is a comma-separated list of preferred IO cell\n"); | 
 |         log("        types in order of preference.\n"); | 
 |         log("\n"); | 
 |     } | 
 |  | 
 |     void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) YS_OVERRIDE | 
 |     { | 
 |         if (a_Args.size() < 3) { | 
 |             log_cmd_error("    Usage: quicklogic_iob <PCF file> <pinmap file> [<io cell specs>]"); | 
 |         } | 
 |  | 
 |         // A map of IO cell types and their port names that should go to a pad | 
 |         std::unordered_map<std::string, IoCellType> ioCellTypes; | 
 |  | 
 |         // Parse io cell specification | 
 |         if (a_Args.size() > 3) { | 
 |  | 
 |             // FIXME: Are these characters set the only ones that can be in | 
 |             // cell / port name ? | 
 |             std::regex re1("^([\\w$]+):([\\w$]+)$"); | 
 |             std::regex re2("^([\\w$]+):([\\w$]+):([\\w,$]+)$"); | 
 |  | 
 |             for (size_t i = 3; i < a_Args.size(); ++i) { | 
 |                 std::cmatch cm; | 
 |  | 
 |                 // No preffered IO cell types | 
 |                 if (std::regex_match(a_Args[i].c_str(), cm, re1)) { | 
 |                     ioCellTypes.emplace(cm[1].str(), IoCellType(cm[1], cm[2])); | 
 |                 } | 
 |  | 
 |                 // With preferred IO cell types | 
 |                 else if (std::regex_match(a_Args[i].c_str(), cm, re2)) { | 
 |                     std::vector<std::string> preferredTypes; | 
 |                     std::stringstream ss(cm[3]); | 
 |  | 
 |                     while (ss.good()) { | 
 |                         std::string field; | 
 |                         std::getline(ss, field, ','); | 
 |  | 
 |                         preferredTypes.push_back(field); | 
 |                     } | 
 |  | 
 |                     ioCellTypes.emplace(cm[1].str(), IoCellType(cm[1], cm[2], preferredTypes)); | 
 |                 } | 
 |  | 
 |                 // Invalid | 
 |                 else { | 
 |                     log_cmd_error("Invalid IO cell+port spec: '%s'\n", a_Args[i].c_str()); | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |         // Use the default IO cells for QuickLogic FPGAs | 
 |         else { | 
 |             ioCellTypes.emplace("inpad", IoCellType("inpad", "P", {"BIDIR", "SDIOMUX"})); | 
 |             ioCellTypes.emplace("outpad", IoCellType("outpad", "P", {"BIDIR", "SDIOMUX"})); | 
 |             ioCellTypes.emplace("bipad", IoCellType("bipad", "P", {"BIDIR", "SDIOMUX"})); | 
 |             ioCellTypes.emplace("ckpad", IoCellType("ckpad", "P", {"CLOCK", "BIDIR", "SDIOMUX"})); | 
 |         } | 
 |  | 
 |         // Get the top module of the design | 
 |         RTLIL::Module *topModule = a_Design->top_module(); | 
 |         if (topModule == nullptr) { | 
 |             log_cmd_error("No top module detected!\n"); | 
 |         } | 
 |  | 
 |         // Read and parse the PCF file | 
 |         log("Loading PCF from '%s'...\n", a_Args[1].c_str()); | 
 |         auto pcfParser = PcfParser(); | 
 |         if (!pcfParser.parse(a_Args[1])) { | 
 |             log_cmd_error("Failed to parse the PCF file!\n"); | 
 |         } | 
 |  | 
 |         // Build a map of net names to constraints | 
 |         std::unordered_map<std::string, const PcfParser::Constraint> constraintMap; | 
 |         for (auto &constraint : pcfParser.getConstraints()) { | 
 |             if (constraintMap.count(constraint.netName) != 0) { | 
 |                 log_cmd_error("The net '%s' is constrained twice!", constraint.netName.c_str()); | 
 |             } | 
 |             constraintMap.emplace(constraint.netName, constraint); | 
 |         } | 
 |  | 
 |         // Read and parse pinmap CSV file | 
 |         log("Loading pinmap CSV from '%s'...\n", a_Args[2].c_str()); | 
 |         auto pinmapParser = PinmapParser(); | 
 |         if (!pinmapParser.parse(a_Args[2])) { | 
 |             log_cmd_error("Failed to parse the pinmap CSV file!\n"); | 
 |         } | 
 |  | 
 |         // Build a map of pad names to entries | 
 |         std::unordered_map<std::string, std::vector<PinmapParser::Entry>> pinmapMap; | 
 |         for (auto &entry : pinmapParser.getEntries()) { | 
 |             if (entry.count("name") != 0) { | 
 |                 auto &name = entry.at("name"); | 
 |  | 
 |                 if (pinmapMap.count(name) == 0) { | 
 |                     pinmapMap[name] = std::vector<PinmapParser::Entry>(); | 
 |                 } | 
 |  | 
 |                 pinmapMap[name].push_back(entry); | 
 |             } | 
 |         } | 
 |  | 
 |         // Check all IO cells | 
 |         log("Processing cells..."); | 
 |         log("\n"); | 
 |         log("  type       | net        | pad        | loc      | type     | instance\n"); | 
 |         log(" ------------+------------+------------+----------+----------+-----------\n"); | 
 |         for (auto cell : topModule->cells()) { | 
 |             auto ysCellType = RTLIL::unescape_id(cell->type); | 
 |  | 
 |             // Not an IO cell | 
 |             if (ioCellTypes.count(ysCellType) == 0) { | 
 |                 continue; | 
 |             } | 
 |  | 
 |             log("  %-10s ", ysCellType.c_str()); | 
 |  | 
 |             std::string netName; | 
 |             std::string padName; | 
 |             std::string locName; | 
 |             std::string cellType; | 
 |  | 
 |             // Get connections to the specified port | 
 |             const auto &ioCellType = ioCellTypes.at(ysCellType); | 
 |             const std::string port = RTLIL::escape_id(ioCellType.port); | 
 |             if (cell->connections().count(port)) { | 
 |  | 
 |                 // Get the sigspec of the connection | 
 |                 auto sigspec = cell->connections().at(port); | 
 |  | 
 |                 // Get the connected wire | 
 |                 for (auto sigbit : sigspec.bits()) { | 
 |                     if (sigbit.wire != nullptr) { | 
 |                         auto wire = sigbit.wire; | 
 |  | 
 |                         // Has to be top level wire | 
 |                         if (wire->port_input || wire->port_output) { | 
 |  | 
 |                             // Check if the wire is constrained. Get pad name. | 
 |                             std::string baseName = RTLIL::unescape_id(wire->name); | 
 |                             std::string netNames[] = { | 
 |                               baseName, | 
 |                               stringf("%s[%d]", baseName.c_str(), sigbit.offset), | 
 |                               stringf("%s(%d)", baseName.c_str(), sigbit.offset), | 
 |                             }; | 
 |  | 
 |                             padName = ""; | 
 |                             netName = ""; | 
 |  | 
 |                             for (auto &name : netNames) { | 
 |                                 if (constraintMap.count(name)) { | 
 |                                     auto constraint = constraintMap.at(name); | 
 |                                     padName = constraint.padName; | 
 |                                     netName = name; | 
 |                                     break; | 
 |                                 } | 
 |                             } | 
 |  | 
 |                             // Check if there is an entry in the pinmap for this pad name | 
 |                             if (pinmapMap.count(padName)) { | 
 |  | 
 |                                 // Choose a correct entry for the cell | 
 |                                 auto entry = choosePinmapEntry(pinmapMap.at(padName), ioCellType); | 
 |  | 
 |                                 // Location string | 
 |                                 if (entry.count("x") && entry.count("y")) { | 
 |                                     locName = stringf("X%sY%s", entry.at("x").c_str(), entry.at("y").c_str()); | 
 |                                 } | 
 |  | 
 |                                 // Cell type | 
 |                                 if (entry.count("type")) { | 
 |                                     cellType = entry.at("type"); | 
 |                                 } | 
 |                             } | 
 |                         } | 
 |                     } | 
 |                 } | 
 |             } | 
 |  | 
 |             log("| %-10s | %-10s | %-8s | %-8s | %s\n", netName.c_str(), padName.c_str(), locName.c_str(), cellType.c_str(), cell->name.c_str()); | 
 |  | 
 |             // Annotate the cell by setting its parameters | 
 |             cell->setParam(RTLIL::escape_id("IO_PAD"), padName); | 
 |             cell->setParam(RTLIL::escape_id("IO_LOC"), locName); | 
 |             cell->setParam(RTLIL::escape_id("IO_TYPE"), cellType); | 
 |         } | 
 |     } | 
 |  | 
 |     PinmapParser::Entry choosePinmapEntry(const std::vector<PinmapParser::Entry> &a_Entries, const IoCellType &a_IoCellType) | 
 |     { | 
 |         // No preferred types, pick the first one | 
 |         if (a_IoCellType.preferredTypes.empty()) { | 
 |             return a_Entries[0]; | 
 |         } | 
 |  | 
 |         // Loop over preferred types | 
 |         for (auto &type : a_IoCellType.preferredTypes) { | 
 |  | 
 |             // Find an entry for that type. If found then return it. | 
 |             for (auto &entry : a_Entries) { | 
 |                 if (type == entry.at("type")) { | 
 |                     return entry; | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |         // No preferred type was found, pick the first one. | 
 |         return a_Entries[0]; | 
 |     } | 
 |  | 
 | } QuicklogicIob; | 
 |  | 
 | PRIVATE_NAMESPACE_END |