Added support for placeholder tags in fasm features.

Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
diff --git a/utils/fasm/src/fasm.cpp b/utils/fasm/src/fasm.cpp
index d6d1acc..d5a3552 100644
--- a/utils/fasm/src/fasm.cpp
+++ b/utils/fasm/src/fasm.cpp
@@ -58,6 +58,28 @@
 
     current_blk_has_prefix_ = true;
     clb_prefix_map_.clear();
+
+    // Get placeholder list (if provided)
+    tags_.clear();
+    if(grid_loc.meta != nullptr && grid_loc.meta->has("fasm_placeholders")) {
+      auto* value = grid_loc.meta->get("fasm_placeholders");
+      VTR_ASSERT(value != nullptr);
+
+      // Parse placeholder definition
+      std::vector<std::string> tag_defs = vtr::split(value->front().as_string(), "\n");
+      for (auto& tag_def: tag_defs) {
+        auto parts = split_fasm_entry(tag_def, "=:", "\t ");
+        if (parts.size() == 0) {
+          continue;
+        }
+
+        VTR_ASSERT(parts.size() == 2);
+
+        VTR_ASSERT(tags_.count(parts.at(0)) == 0);
+        tags_[parts.at(0)] = parts.at(1);
+      }
+    }
+
     std::string grid_prefix;
     if(grid_loc.meta != nullptr && grid_loc.meta->has("fasm_prefix")) {
       auto* value = grid_loc.meta->get("fasm_prefix");
@@ -719,10 +741,14 @@
     std::string feature;
     os >> feature;
     if(os) {
+      std::string out_feature;
       if(have_clb_prefix) {
-        os_ << blk_prefix_ << clb_prefix;
+        out_feature += blk_prefix_;
+        out_feature += clb_prefix;
       }
-      os_ << feature << std::endl;
+      out_feature += feature;
+      // Substitute tags
+      os_ << substitute_tags(out_feature, tags_) << std::endl;
     }
   }
 
diff --git a/utils/fasm/src/fasm.h b/utils/fasm/src/fasm.h
index 471a8c2..1326c4c 100644
--- a/utils/fasm/src/fasm.h
+++ b/utils/fasm/src/fasm.h
@@ -94,6 +94,8 @@
       std::vector<t_pb_graph_pin**> pb_graph_pin_lookup_from_index_by_type_;
       std::map<const t_pb_type*, std::vector<std::pair<std::string, LutOutputDefinition>>> lut_definitions_;
       std::map<const t_pb_type*, Parameters> parameters_;
+
+      std::map<const std::string, std::string> tags_;
 };
 
 } // namespace fasm
diff --git a/utils/fasm/src/fasm_utils.cpp b/utils/fasm/src/fasm_utils.cpp
index fddb62a..bc397d0 100644
--- a/utils/fasm/src/fasm_utils.cpp
+++ b/utils/fasm/src/fasm_utils.cpp
@@ -1,6 +1,8 @@
 #include "fasm_utils.h"
 #include "vpr_utils.h"
 
+#include <regex>
+
 namespace fasm {
 
 void parse_name_with_optional_index(const std::string in, std::string *name, int *index) {
@@ -30,4 +32,57 @@
   return vtr::split(entry, delims);
 }
 
+std::vector<std::string> find_tags_in_feature (const std::string& a_String) {
+    const std::regex regex ("(\\{[a-zA-Z0-9]+\\})");
+
+    std::vector<std::string> tags;
+    std::string str(a_String);
+    std::smatch match;
+
+    // Search for tags
+    while (std::regex_search(str, match, regex)) {
+        std::string tag(match.str());
+
+        // Strip braces
+        tags.push_back(tag.substr(1, tag.length()-2));
+        str = match.suffix().str();
+    }
+
+    return tags;
+}
+
+std::string substitute_tags (const std::string& a_Feature, const std::map<const std::string, std::string>& a_Tags) {
+
+    // First list tags that are given in the feature string
+    auto tags = find_tags_in_feature(a_Feature);
+    if (tags.empty()) {
+        return a_Feature;
+    }
+
+    // Check if those tags are defined, If not then throw an error
+    bool have_errors = false;
+    for (auto tag: tags) {
+        if (a_Tags.count(tag) == 0) {
+            vtr::printf_error(__FILE__, __LINE__, "fasm placeholder '%s' not defined!", tag.c_str());
+            have_errors = true;
+        }
+    }
+
+    if (have_errors) {
+        vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__,
+            "fasm feature '%s' contains placeholders that are not defined for the tile that it is used in!",
+            a_Feature.c_str()
+        );
+    }
+
+    // Substitute tags
+    std::string feature(a_Feature);
+    for (auto tag: tags) {
+        std::regex regex("\\{" + tag + "\\}");
+        feature = std::regex_replace(feature, regex, a_Tags.at(tag));
+    }
+
+    return feature;
+}
+
 } // namespace fasm
diff --git a/utils/fasm/src/fasm_utils.h b/utils/fasm/src/fasm_utils.h
index 99f82c7..983ed9e 100644
--- a/utils/fasm/src/fasm_utils.h
+++ b/utils/fasm/src/fasm_utils.h
@@ -3,6 +3,7 @@
 
 #include <string>
 #include <vector>
+#include <map>
 
 namespace fasm {
 
@@ -22,6 +23,15 @@
                                                  std::string delims,
                                                  std::string ignore);
 
+// Substitutes tags found in a string with their values provided by the map.
+// Thorws an error if a tag is found in the string and its value is not present
+// in the map.
+//
+// a_Feature - Fasm feature string (or any other string)
+// a_Tags    - Map with tags and their values
+std::string substitute_tags (const std::string& a_Feature,
+                             const std::map<const std::string, std::string>& a_Tags);
+
 } // namespace fasm
 
 #endif /* UTILS_FASM_FASM_UTILS_H_ */
diff --git a/utils/fasm/test/test_fasm.cpp b/utils/fasm/test/test_fasm.cpp
index 9be59ef..6ec278d 100644
--- a/utils/fasm/test/test_fasm.cpp
+++ b/utils/fasm/test/test_fasm.cpp
@@ -7,6 +7,7 @@
 #include "arch_util.h"
 #include "rr_graph_writer.h"
 #include <sstream>
+#include <fstream>
 
 static constexpr const char kArchFile[] = "test_fasm_arch.xml";
 static constexpr const char kRrGraphFile[] = "test_fasm_rrgraph.xml";
@@ -78,6 +79,16 @@
 
     fasm_string.seekg(0);
 
+    std::ofstream fasm_file("output.fasm", std::ios_base::out);
+    while(fasm_string) {
+        std::string line;
+        std::getline(fasm_string, line);
+        fasm_file << line << std::endl;
+    }
+
+    fasm_string.clear();
+    fasm_string.seekg(0);
+
     std::set<std::tuple<int, int, short>> routing_edges;
     bool found_lut5 = false;
     bool found_lut6 = false;
@@ -99,13 +110,13 @@
             continue;
         }
 
-        if(!found_mux1 && line == "CLB.FLE9.OUT_MUX.LUT") {
+        if(!found_mux1 && line.find(".OUT_MUX.LUT") != std::string::npos) {
             found_mux1 = true;
         }
-        if(!found_mux2 && line == "CLB.FLE9.DISABLE_FF") {
+        if(!found_mux2 && line.find(".DISABLE_FF") != std::string::npos) {
             found_mux2 = true;
         }
-        if(!found_mux3 && line == "CLB.FLE9.IN0") {
+        if(!found_mux3 && line.find(".IN0") != std::string::npos) {
             found_mux3 = true;
         }
 
diff --git a/utils/fasm/test/test_fasm_arch.xml b/utils/fasm/test/test_fasm_arch.xml
index dd5ac4b..6f6cf2a 100644
--- a/utils/fasm/test/test_fasm_arch.xml
+++ b/utils/fasm/test/test_fasm_arch.xml
@@ -28,20 +28,100 @@
     </tile>
   </tiles>
   <layout>
-    <fixed_layout height="10" width="10" name="test">
+    <fixed_layout height="6" width="6" name="test">
       <perimeter type="io" priority="100">
         <metadata>
           <meta name="type">io</meta>
         </metadata>
       </perimeter>
       <corners type="EMPTY" priority="101"/>
-      <fill type="clb" priority="10">
-        <metadata>
-          <meta name="fasm_prefix">CLB</meta>
-        </metadata>
-      </fill>
-      <col type="EMPTY" startx="6" repeatx="8" starty="1" priority="19"/>
-      <col type="EMPTY" startx="2" repeatx="8" starty="1" priority="19"/>
+
+      <single type="clb" priority="10" x="1" y="1">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X1Y1
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="1" y="2">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X1Y2
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="1" y="3">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X1Y3
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="1" y="4">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X1Y4
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+
+      <single type="clb" priority="10" x="2" y="1">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X2Y1
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="2" y="2">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X2Y2
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="2" y="3">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X2Y3
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="2" y="4">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X2Y4
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+
+      <single type="clb" priority="10" x="3" y="1">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X3Y1
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="3" y="2">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X3Y2
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="3" y="3">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X3Y3
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="3" y="4">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X3Y4
+      </meta>  <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+
+      <single type="clb" priority="10" x="4" y="1">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X4Y1
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="4" y="2">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X4Y2
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="4" y="3">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X4Y3
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+      <single type="clb" priority="10" x="4" y="4">
+      <metadata> <meta name="fasm_placeholders">
+        LOC=X4Y4
+      </meta> <meta name="fasm_prefix">CLB</meta> </metadata>
+      </single>
+
+<!--      <col type="EMPTY" startx="6" repeatx="8" starty="1" priority="19"/>
+      <col type="EMPTY" startx="2" repeatx="8" starty="1" priority="19"/> -->
     </fixed_layout>
   </layout>
   <device>
@@ -267,7 +347,7 @@
         </mode>
         <metadata>
           <meta name="fasm_prefix">
-            FLE0 FLE1 FLE2 FLE3 FLE4 FLE5 FLE6 FLE7 FLE8 FLE9
+            FLE0_{LOC} FLE1_{LOC} FLE2_{LOC} FLE3_{LOC} FLE4_{LOC} FLE5_{LOC} FLE6_{LOC} FLE7_{LOC} FLE8_{LOC} FLE9_{LOC}
           </meta>
         </metadata>
       </pb_type>