Merge pull request #417 from antmicro/rk/defaults-defines

Add systemverilog_defines, systemverilog_defaults
diff --git a/systemverilog-plugin/tests/Makefile b/systemverilog-plugin/tests/Makefile
index 1843a05..849a9f9 100644
--- a/systemverilog-plugin/tests/Makefile
+++ b/systemverilog-plugin/tests/Makefile
@@ -18,7 +18,9 @@
 		break_continue \
 		separate-compilation \
 		debug-flag \
-		report-flag
+		report-flag \
+		defines \
+		defaults
 
 include $(shell pwd)/../../Makefile_test.common
 
@@ -27,3 +29,5 @@
 separate-compilation_verify = true
 debug-flag_verify = true
 report-flag_verify = true
+defaults_verify = true
+defines_verify = true
diff --git a/systemverilog-plugin/tests/defaults/defaults.tcl b/systemverilog-plugin/tests/defaults/defaults.tcl
new file mode 100644
index 0000000..a363895
--- /dev/null
+++ b/systemverilog-plugin/tests/defaults/defaults.tcl
@@ -0,0 +1,23 @@
+yosys -import
+if { [info procs read_uhdm] == {} } { plugin -i systemverilog }
+yosys -import  ;# ingest plugin commands
+
+set TMP_DIR /tmp
+if { [info exists ::env(TMPDIR) ] } {
+  set TMP_DIR $::env(TMPDIR)
+}
+
+# Define forbidden value
+systemverilog_defaults -add -DPAKALA
+# Stash it
+systemverilog_defaults -push
+systemverilog_defaults -clear
+read_systemverilog $::env(DESIGN_TOP).v
+# Allow parsing the module again
+delete top
+systemverilog_defaults -pop
+# Skip check for forbidden value
+systemverilog_defaults -add -Pbypass=1
+read_systemverilog $::env(DESIGN_TOP).v
+hierarchy
+write_verilog
diff --git a/systemverilog-plugin/tests/defaults/defaults.v b/systemverilog-plugin/tests/defaults/defaults.v
new file mode 100644
index 0000000..f565a77
--- /dev/null
+++ b/systemverilog-plugin/tests/defaults/defaults.v
@@ -0,0 +1,28 @@
+// Copyright 2020-2022 F4PGA Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+module top #(
+  parameter bit bypass = 0
+)(
+  input clk,
+  output out
+);
+
+`ifdef PAKALA
+  initial if(!bypass) $stop("Defined forbidden value");
+`endif
+  assign out = clk;
+endmodule
diff --git a/systemverilog-plugin/tests/defines/defines.tcl b/systemverilog-plugin/tests/defines/defines.tcl
new file mode 100644
index 0000000..030c1ad
--- /dev/null
+++ b/systemverilog-plugin/tests/defines/defines.tcl
@@ -0,0 +1,15 @@
+yosys -import
+if { [info procs read_uhdm] == {} } { plugin -i systemverilog }
+yosys -import  ;# ingest plugin commands
+
+set TMP_DIR /tmp
+if { [info exists ::env(TMPDIR) ] } {
+  set TMP_DIR $::env(TMPDIR)
+}
+
+systemverilog_defines -DPONA
+systemverilog_defines -DPAKALA
+systemverilog_defines -UPAKALA
+read_systemverilog $::env(DESIGN_TOP).v
+hierarchy
+write_verilog
diff --git a/systemverilog-plugin/tests/defines/defines.v b/systemverilog-plugin/tests/defines/defines.v
new file mode 100644
index 0000000..528e9f1
--- /dev/null
+++ b/systemverilog-plugin/tests/defines/defines.v
@@ -0,0 +1,29 @@
+// Copyright 2020-2022 F4PGA Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+module top (
+  input clk,
+  output out
+);
+
+`ifndef PONA
+  initial $stop("Define failed");
+`endif
+`ifdef PAKALA
+  initial $stop("Undefine failed");
+`endif
+  assign out = clk;
+endmodule
diff --git a/systemverilog-plugin/uhdmsurelogastfrontend.cc b/systemverilog-plugin/uhdmsurelogastfrontend.cc
index c11c861..3c1c2f6 100644
--- a/systemverilog-plugin/uhdmsurelogastfrontend.cc
+++ b/systemverilog-plugin/uhdmsurelogastfrontend.cc
@@ -31,6 +31,8 @@
 #endif
 #include <memory>
 
+#include <list>
+
 #include "Surelog/ErrorReporting/Report.h"
 #include "Surelog/surelog.h"
 
@@ -42,6 +44,13 @@
 
 YOSYS_NAMESPACE_BEGIN
 
+// Store systemverilog defaults to be passed for every invocation of read_systemverilog
+static std::vector<std::string> systemverilog_defaults;
+static std::list<std::vector<std::string>> systemverilog_defaults_stack;
+
+// Store global definitions for top-level defines
+static std::vector<std::string> systemverilog_defines;
+
 // SURELOG::scompiler wrapper.
 // Owns UHDM/VPI resources used by designs returned from `execute`
 class Compiler
@@ -119,9 +128,29 @@
     AST::AstNode *parse(std::string filename) override
     {
         std::vector<const char *> cstrings;
-        cstrings.reserve(this->args.size());
-        for (size_t i = 0; i < this->args.size(); ++i)
+        bool link = false;
+        cstrings.reserve(this->args.size() + systemverilog_defaults.size() + systemverilog_defines.size());
+        for (size_t i = 0; i < this->args.size(); ++i) {
             cstrings.push_back(const_cast<char *>(this->args[i].c_str()));
+            if (this->args[i] == "-link")
+                link = true;
+        }
+
+        if (!link) {
+            // Add systemverilog defaults args
+            for (size_t i = 0; i < systemverilog_defaults.size(); ++i) {
+                // Convert args to surelog compatible
+                if (systemverilog_defaults[i] == "-defer")
+                    this->shared.defer = true;
+                // Pass any remainings args directly to surelog
+                else
+                    cstrings.push_back(const_cast<char *>(systemverilog_defaults[i].c_str()));
+            }
+
+            // Add systemverilog defines args
+            for (size_t i = 0; i < systemverilog_defines.size(); ++i)
+                cstrings.push_back(const_cast<char *>(systemverilog_defines[i].c_str()));
+        }
 
         auto symbolTable = std::make_unique<SURELOG::SymbolTable>();
         auto errors = std::make_unique<SURELOG::ErrorContainer>(symbolTable.get());
@@ -182,6 +211,9 @@
             this->shared.report.write(this->report_directory);
         }
 
+        // FIXME: Check and reset remaining shared data
+        this->shared.top_nodes.clear();
+        this->shared.nonSynthesizableObjects.clear();
         return current_ast;
     }
     void call_log_header(RTLIL::Design *design) override { log_header(design, "Executing Verilog with UHDM frontend.\n"); }
@@ -198,7 +230,164 @@
         log("Read SystemVerilog files using Surelog into the current design\n");
         log("\n");
         this->print_read_options();
+        log("    -Ipath\n");
+        log("        add include path.\n");
+        log("\n");
+        log("    -Pparameter=value\n");
+        log("        define parameter as value.\n");
+        log("\n");
     }
 } UhdmSystemVerilogFrontend;
 
+struct SystemVerilogDefaults : public Pass {
+    SystemVerilogDefaults() : Pass("systemverilog_defaults", "set default options for read_systemverilog") {}
+    void help() override
+    {
+        //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+        log("\n");
+        log("    systemverilog_defaults -add [options]\n");
+        log("\n");
+        log("Add the specified options to the list of default options to read_systemverilog.\n");
+        log("\n");
+        log("\n");
+        log("    systemverilog_defaults -clear\n");
+        log("\n");
+        log("Clear the list of Systemverilog default options.\n");
+        log("\n");
+        log("\n");
+        log("    systemverilog_defaults -push\n");
+        log("    systemverilog_defaults -pop\n");
+        log("\n");
+        log("Push or pop the list of default options to a stack. Note that -push does\n");
+        log("not imply -clear.\n");
+        log("\n");
+    }
+    void execute(std::vector<std::string> args, RTLIL::Design *) override
+    {
+        if (args.size() < 2)
+            cmd_error(args, 1, "Missing argument.");
+
+        if (args[1] == "-add") {
+            systemverilog_defaults.insert(systemverilog_defaults.end(), args.begin() + 2, args.end());
+            return;
+        }
+
+        if (args.size() != 2)
+            cmd_error(args, 2, "Extra argument.");
+
+        if (args[1] == "-clear") {
+            systemverilog_defaults.clear();
+            return;
+        }
+
+        if (args[1] == "-push") {
+            systemverilog_defaults_stack.push_back(systemverilog_defaults);
+            return;
+        }
+
+        if (args[1] == "-pop") {
+            if (systemverilog_defaults_stack.empty()) {
+                systemverilog_defaults.clear();
+            } else {
+                systemverilog_defaults.swap(systemverilog_defaults_stack.back());
+                systemverilog_defaults_stack.pop_back();
+            }
+            return;
+        }
+    }
+} SystemVerilogDefaults;
+
+struct SystemVerilogDefines : public Pass {
+    SystemVerilogDefines() : Pass("systemverilog_defines", "define and undefine systemverilog defines")
+    {
+        systemverilog_defines.push_back("-DYOSYS=1");
+    }
+    void help() override
+    {
+        //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+        log("\n");
+        log("    systemverilog_defines [options]\n");
+        log("\n");
+        log("Define and undefine systemverilog preprocessor macros.\n");
+        log("\n");
+        log("    -Dname[=definition]\n");
+        log("        define the preprocessor symbol 'name' and set its optional value\n");
+        log("        'definition'\n");
+        log("\n");
+        log("    -Uname[=definition]\n");
+        log("        undefine the preprocessor symbol 'name'\n");
+        log("\n");
+        log("    -reset\n");
+        log("        clear list of defined preprocessor symbols\n");
+        log("\n");
+        log("    -list\n");
+        log("        list currently defined preprocessor symbols\n");
+        log("\n");
+    }
+    void remove(const std::string name)
+    {
+        auto it = systemverilog_defines.begin();
+        while (it != systemverilog_defines.end()) {
+            std::string nm;
+            size_t equal = (*it).find('=', 2);
+            if (equal == std::string::npos)
+                nm = (*it).substr(2, std::string::npos);
+            else
+                nm = (*it).substr(2, equal - 2);
+            if (name == nm)
+                systemverilog_defines.erase(it);
+            else
+                it++;
+        }
+    }
+    void dump(void)
+    {
+        for (size_t i = 0; i < systemverilog_defines.size(); ++i) {
+            std::string name, value = "";
+            size_t equal = systemverilog_defines[i].find('=', 2);
+            name = systemverilog_defines[i].substr(2, equal - 2);
+            if (equal != std::string::npos)
+                value = systemverilog_defines[i].substr(equal + 1, std::string::npos);
+            Yosys::log("`define %s %s\n", name.c_str(), value.c_str());
+        }
+    }
+    void execute(std::vector<std::string> args, RTLIL::Design *design) override
+    {
+        size_t argidx;
+        for (argidx = 1; argidx < args.size(); argidx++) {
+            std::string arg = args[argidx];
+            if (arg == "-D" && argidx + 1 < args.size()) {
+                systemverilog_defines.push_back("-D" + args[++argidx]);
+                continue;
+            }
+            if (arg.compare(0, 2, "-D") == 0) {
+                systemverilog_defines.push_back(arg);
+                continue;
+            }
+            if (arg == "-U" && argidx + 1 < args.size()) {
+                std::string name = args[++argidx];
+                this->remove(name);
+                continue;
+            }
+            if (arg.compare(0, 2, "-U") == 0) {
+                std::string name = arg.substr(2);
+                this->remove(name);
+                continue;
+            }
+            if (arg == "-reset") {
+                systemverilog_defines.erase(systemverilog_defines.begin() + 1, systemverilog_defines.end());
+                continue;
+            }
+            if (arg == "-list") {
+                this->dump();
+                continue;
+            }
+            break;
+        }
+
+        if (args.size() != argidx)
+            cmd_error(args, argidx, "Extra argument.");
+    }
+} SystemVerilogDefines;
+
 YOSYS_NAMESPACE_END