| /* |
| * 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 |
| * |
| */ |
| |
| #include "UhdmAst.h" |
| #include "frontends/ast/ast.h" |
| #include "kernel/yosys.h" |
| #include "uhdmcommonfrontend.h" |
| |
| #if defined(_MSC_VER) |
| #include <direct.h> |
| #include <process.h> |
| #else |
| #include <sys/param.h> |
| #include <unistd.h> |
| #endif |
| #include <memory> |
| |
| #include <list> |
| |
| #include "Surelog/ErrorReporting/Report.h" |
| #include "Surelog/surelog.h" |
| |
| namespace UHDM |
| { |
| extern void visit_object(vpiHandle obj_h, int indent, const char *relation, std::set<const BaseClass *> *visited, std::ostream &out, |
| bool shallowVisit = false); |
| } |
| |
| namespace systemverilog_plugin |
| { |
| |
| using namespace ::Yosys; |
| |
| // 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 |
| { |
| public: |
| Compiler() = default; |
| ~Compiler() |
| { |
| if (this->scompiler) { |
| SURELOG::shutdown_compiler(this->scompiler); |
| } |
| } |
| |
| const std::vector<vpiHandle> &execute(std::unique_ptr<SURELOG::ErrorContainer> errors, std::unique_ptr<SURELOG::CommandLineParser> clp) |
| { |
| log_assert(!this->errors && !this->clp && !this->scompiler); |
| |
| bool success = true; |
| bool noFatalErrors = true; |
| unsigned int codedReturn = 0; |
| clp->setWriteUhdm(false); |
| errors->printMessages(clp->muteStdout()); |
| if (success && (!clp->help())) { |
| this->scompiler = SURELOG::start_compiler(clp.get()); |
| if (!this->scompiler) |
| codedReturn |= 1; |
| this->designs.push_back(SURELOG::get_uhdm_design(this->scompiler)); |
| } |
| SURELOG::ErrorContainer::Stats stats; |
| if (!clp->help()) { |
| stats = errors->getErrorStats(); |
| if (stats.nbFatal) |
| codedReturn |= 1; |
| if (stats.nbSyntax) |
| codedReturn |= 2; |
| } |
| bool noFErrors = true; |
| if (!clp->help()) |
| noFErrors = errors->printStats(stats, clp->muteStdout()); |
| if (noFErrors == false) { |
| noFatalErrors = false; |
| } |
| if ((!noFatalErrors) || (!success) || (errors->getErrorStats().nbError)) |
| codedReturn |= 1; |
| if (codedReturn) { |
| log_error("Error when parsing design. Aborting!\n"); |
| } |
| |
| this->clp = std::move(clp); |
| this->errors = std::move(errors); |
| |
| return this->designs; |
| } |
| |
| private: |
| std::unique_ptr<SURELOG::ErrorContainer> errors = nullptr; |
| std::unique_ptr<SURELOG::CommandLineParser> clp = nullptr; |
| SURELOG::scompiler *scompiler = nullptr; |
| std::vector<vpiHandle> designs = {}; |
| }; |
| |
| struct UhdmSurelogAstFrontend : public UhdmCommonFrontend { |
| UhdmSurelogAstFrontend(std::string name, std::string short_help) : UhdmCommonFrontend(name, short_help) {} |
| UhdmSurelogAstFrontend() : UhdmCommonFrontend("verilog_with_uhdm", "generate/read UHDM file") {} |
| |
| void help() override |
| { |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| log("\n"); |
| log(" read_verilog_with_uhdm [options] [filenames]\n"); |
| log("\n"); |
| log("Read SystemVerilog files using Surelog into the current design\n"); |
| log("\n"); |
| this->print_read_options(); |
| } |
| |
| AST::AstNode *parse(std::string filename) override |
| { |
| std::vector<const char *> cstrings; |
| bool link = false; |
| if (this->shared.formal) { |
| systemverilog_defines.push_back("-DFORMAL=1"); |
| } else { |
| systemverilog_defines.push_back("-DSYNTHESIS=1"); |
| } |
| 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()); |
| auto clp = std::make_unique<SURELOG::CommandLineParser>(errors.get(), symbolTable.get(), false, false); |
| bool success = clp->parseCommandLine(cstrings.size(), &cstrings[0]); |
| if (!success) { |
| log_error("Error parsing Surelog arguments!\n"); |
| } |
| // Force -parse flag settings even if it wasn't specified |
| clp->setwritePpOutput(true); |
| clp->setParse(true); |
| clp->fullSVMode(true); |
| clp->setCacheAllowed(true); |
| if (this->shared.defer) { |
| clp->setCompile(false); |
| clp->setElaborate(false); |
| clp->setSepComp(true); |
| } else { |
| clp->setCompile(true); |
| clp->setElaborate(true); |
| } |
| if (this->shared.link) { |
| clp->setLink(true); |
| } |
| |
| Compiler compiler; |
| const auto &uhdm_designs = compiler.execute(std::move(errors), std::move(clp)); |
| |
| if (this->shared.debug_flag || !this->report_directory.empty()) { |
| for (auto design : uhdm_designs) { |
| std::ofstream null_stream; |
| UHDM::visit_object(design, 1, "", &this->shared.report.unhandled, this->shared.debug_flag ? std::cout : null_stream); |
| } |
| } |
| |
| // on parse_only mode, don't try to load design |
| // into yosys |
| if (this->shared.parse_only) |
| return nullptr; |
| |
| if (this->shared.defer && !this->shared.link) |
| return nullptr; |
| |
| // FIXME: SynthSubset annotation is incompatible with separate compilation |
| // `-defer` turns elaboration off, so check for it |
| // Should be called 1. for normal flow 2. after finishing with `-link` |
| if (!this->shared.defer) { |
| UHDM::Serializer serializer; |
| UHDM::SynthSubset *synthSubset = |
| make_new_object_with_optional_extra_true_arg<UHDM::SynthSubset>(&serializer, this->shared.nonSynthesizableObjects, false); |
| synthSubset->listenDesigns(uhdm_designs); |
| delete synthSubset; |
| } |
| |
| UhdmAst uhdm_ast(this->shared); |
| AST::AstNode *current_ast = uhdm_ast.visit_designs(uhdm_designs); |
| if (!this->report_directory.empty()) { |
| 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"); } |
| } UhdmSurelogAstFrontend; |
| |
| struct UhdmSystemVerilogFrontend : public UhdmSurelogAstFrontend { |
| UhdmSystemVerilogFrontend() : UhdmSurelogAstFrontend("systemverilog", "read SystemVerilog files") {} |
| void help() override |
| { |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| |
| log("\n"); |
| log(" read_systemverilog [options] [filenames]\n"); |
| log("\n"); |
| 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; |
| |
| } // namespace systemverilog_plugin |