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