Added IO cell type preference

Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
diff --git a/ql-iob-plugin/pinmap_parser.cc b/ql-iob-plugin/pinmap_parser.cc
index 8215ca7..89a8da8 100644
--- a/ql-iob-plugin/pinmap_parser.cc
+++ b/ql-iob-plugin/pinmap_parser.cc
@@ -73,6 +73,10 @@
         std::string line;
         std::getline(*a_Stream, line);
 
+        if (line.empty()) {
+            continue;
+        }
+
         // Parse datafields
         auto data = getFields(line);
 
diff --git a/ql-iob-plugin/ql-iob.cc b/ql-iob-plugin/ql-iob.cc
index 3129d31..e382cea 100644
--- a/ql-iob-plugin/ql-iob.cc
+++ b/ql-iob-plugin/ql-iob.cc
@@ -25,6 +25,7 @@
 #include "kernel/rtlil.h"
 
 #include <regex>
+#include <sstream>
 
 USING_YOSYS_NAMESPACE
 PRIVATE_NAMESPACE_BEGIN
@@ -37,6 +38,18 @@
 
 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") {
             register_in_tcl_interpreter(pass_name);
@@ -62,11 +75,15 @@
         log("    - <pinmap file>\n");
         log("        Path to a pinmap CSV file with package pin map\n");
         log("\n");
-        log("    - <io cell specs>\n");
-        log("        A space-separated list of <io cell type>:<port>. Each\n");
-        log("        entry defines a type of IO cell to be affected an its port\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 {
@@ -75,20 +92,41 @@
         }
 
         // A map of IO cell types and their port names that should go to a pad
-        std::unordered_map<std::string, std::string> ioCellTypes;
+        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 re("^([\\w$]+):([\\w$]+)$");
+            std::regex re1("^([\\w$]+):([\\w$]+)$");
+            std::regex re2("^([\\w$]+):([\\w$]+):([\\w,$]+)$");
 
             for (size_t i=3; i<a_Args.size(); ++i) {
                 std::cmatch cm;
-                if (std::regex_match(a_Args[i].c_str(), cm, re)) {
-                    ioCellTypes.insert(std::make_pair(cm[1].str(), cm[2].str()));
+
+                // 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());
                 }
@@ -97,10 +135,10 @@
 
         // Use the default IO cells for QuickLogic FPGAs
         else {
-            ioCellTypes.insert(std::make_pair("inpad",  "P"));
-            ioCellTypes.insert(std::make_pair("outpad", "P"));
-            ioCellTypes.insert(std::make_pair("bipad",  "P"));
-            ioCellTypes.insert(std::make_pair("ckpad",  "P"));
+            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
@@ -133,18 +171,24 @@
         }
 
         // Build a map of pad names to entries
-        std::unordered_map<std::string, const PinmapParser::Entry> pinmapMap;
+        std::unordered_map<std::string, std::vector<PinmapParser::Entry>> pinmapMap;
         for (auto& entry : pinmapParser.getEntries()) {
             if (entry.count("name") != 0) {
-                pinmapMap.emplace(entry.at("name"), entry);
+                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       | instance             | net        | pad        | loc      | cell     \n");
-        log(" ------------+----------------------+------------+------------+----------+----------\n");
+        log("  type       | net        | pad        | loc      | type     | instance\n");
+        log(" ------------+------------+------------+----------+----------+-----------\n");
         for (auto cell : topModule->cells()) {
             auto ysCellType = RTLIL::unescape_id(cell->type); 
 
@@ -153,7 +197,7 @@
                 continue;
             }
 
-            log("  %-10s | %-20s ", ysCellType.c_str(), cell->name.c_str());
+            log("  %-10s ", ysCellType.c_str());
 
             std::string netName;
             std::string padName;
@@ -161,7 +205,8 @@
             std::string cellType;
 
             // Get connections to the specified port
-            std::string port = RTLIL::escape_id(ioCellTypes.at(ysCellType));
+            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
@@ -186,8 +231,12 @@
                             // Check if there is an entry in the pinmap for this pad name
                             if (pinmapMap.count(constraint.padName)) {
 
-                                // Get the entry
-                                auto entry = pinmapMap.at(constraint.padName);
+                                // Choose a correct entry for the cell
+                                auto entry = choosePinmapEntry(
+                                    pinmapMap.at(constraint.padName),
+                                    ioCellType
+                                );
+
                                 padName = constraint.padName;
 
                                 // Location string
@@ -208,11 +257,12 @@
                 }
             }
 
-            log("| %-10s | %-10s | %-8s | %s\n",
+            log("| %-10s | %-10s | %-8s | %-8s | %s\n",
                 netName.c_str(),
                 padName.c_str(),
                 locName.c_str(),
-                cellType.c_str()
+                cellType.c_str(),
+                cell->name.c_str()
             );
 
             // Annotate the cell by setting its parameters
@@ -222,6 +272,30 @@
         }
     }
 
+    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