Merge branch 'master' into add_fifo_bram_macros
diff --git a/Makefile b/Makefile index fc39ff4..70433f7 100644 --- a/Makefile +++ b/Makefile
@@ -71,9 +71,9 @@ CLANG_FORMAT ?= clang-format-8 .PHONY: format format: - find . \( -name "*.h" -o -name "*.cc" \) -and -not -path './third_party/*' -print0 | xargs -0 -P $$(nproc) ${CLANG_FORMAT} -style=file -i + find . \( -name "*.h" -o -name "*.cc" \) -and -not -path '*/third_party/*' -print0 | xargs -0 -P $$(nproc) ${CLANG_FORMAT} -style=file -i VERIBLE_FORMAT ?= verible-verilog-format .PHONY: format-verilog format-verilog: - find */tests \( -name "*.v" -o -name "*.sv" \) -and -not -path './third_party/*' -print0 | xargs -0 $(VERIBLE_FORMAT) --inplace + find */tests \( -name "*.v" -o -name "*.sv" \) -and -not -path '*/third_party/*' -print0 | xargs -0 $(VERIBLE_FORMAT) --inplace
diff --git a/environment.yml b/environment.yml index a4cf46c..dc51454 100644 --- a/environment.yml +++ b/environment.yml
@@ -20,5 +20,5 @@ - litex-hub dependencies: - litex-hub::yosys=0.17_7_g990c9b8e1=20220512_085338_py37 - - litex-hub::surelog + - litex-hub::surelog=0.0_5519_g900fb2499=20221223_060448_py37 - litex-hub::iverilog
diff --git a/ql-qlf-plugin/synth_quicklogic.cc b/ql-qlf-plugin/synth_quicklogic.cc index fbbacb5..b6b475e 100644 --- a/ql-qlf-plugin/synth_quicklogic.cc +++ b/ql-qlf-plugin/synth_quicklogic.cc
@@ -554,11 +554,7 @@ if (check_label("blif")) { if (!blif_file.empty()) { - if (inferAdder) { - run(stringf("write_blif -param %s", help_mode ? "<file-name>" : blif_file.c_str())); - } else { - run(stringf("write_blif %s", help_mode ? "<file-name>" : blif_file.c_str())); - } + run(stringf("write_blif -param %s", help_mode ? "<file-name>" : blif_file.c_str())); } }
diff --git a/systemverilog-plugin/Makefile b/systemverilog-plugin/Makefile index a1d69e1..af5320d 100644 --- a/systemverilog-plugin/Makefile +++ b/systemverilog-plugin/Makefile
@@ -18,11 +18,11 @@ NAME = systemverilog SOURCES = UhdmAst.cc \ - UhdmAstUpstream.cc \ uhdmastfrontend.cc \ uhdmcommonfrontend.cc \ uhdmsurelogastfrontend.cc \ - uhdmastreport.cc + uhdmastreport.cc \ + third_party/yosys/const2ast.cc # Directory to search for Surelog and UHDM libraries UHDM_INSTALL_DIR ?= /usr/local
diff --git a/systemverilog-plugin/UhdmAst.cc b/systemverilog-plugin/UhdmAst.cc index 20e68a0..00816d6 100644 --- a/systemverilog-plugin/UhdmAst.cc +++ b/systemverilog-plugin/UhdmAst.cc
@@ -14,9 +14,33 @@ #include <uhdm/uhdm.h> #include <uhdm/vpi_user.h> -#include "UhdmAstUpstream.h" +#include "third_party/yosys/const2ast.h" YOSYS_NAMESPACE_BEGIN +namespace VERILOG_FRONTEND +{ +extern bool sv_mode; +} +YOSYS_NAMESPACE_END + +namespace systemverilog_plugin +{ + +using namespace ::Yosys; + +namespace AST +{ +using namespace ::Yosys::AST; + +namespace Extended +{ +enum AstNodeTypeExtended { + AST_DOT = ::Yosys::AST::AST_BIND + 1, // here we always want to point to the last element of yosys' AstNodeType + AST_BREAK, + AST_CONTINUE +}; +} +} // namespace AST /*static*/ const IdString &UhdmAst::partial() { @@ -96,6 +120,13 @@ return objectName; } +static AST::AstNode *mkconst_real(double d) +{ + AST::AstNode *node = new AST::AstNode(AST::AST_REALVALUE); + node->realvalue = d; + return node; +} + static AST::AstNode *make_range(int left, int right, bool is_signed = false) { // generate a pre-validated range node for a fixed signal range. @@ -486,7 +517,7 @@ AST::AstNode *struct_range = nullptr; for (auto c : search_node->children) { - if (c->type == static_cast<int>(AST::AST_DOT)) { + if (c->type == static_cast<int>(AST::Extended::AST_DOT)) { // There should be only 1 AST_DOT node children log_assert(!sub_dot); sub_dot = expand_dot(current_struct_elem, c); @@ -851,8 +882,8 @@ static void simplify(AST::AstNode *current_node, AST::AstNode *parent_node) { - auto dot_it = - std::find_if(current_node->children.begin(), current_node->children.end(), [](auto c) { return c->type == static_cast<int>(AST::AST_DOT); }); + auto dot_it = std::find_if(current_node->children.begin(), current_node->children.end(), + [](auto c) { return c->type == static_cast<int>(AST::Extended::AST_DOT); }); AST::AstNode *dot = (dot_it != current_node->children.end()) ? *dot_it : nullptr; AST::AstNode *expanded = nullptr; @@ -875,8 +906,8 @@ break; } else { current_node->str += "." + dot->str.substr(1); - dot_it = - std::find_if(dot->children.begin(), dot->children.end(), [](auto c) { return c->type == static_cast<int>(AST::AST_DOT); }); + dot_it = std::find_if(dot->children.begin(), dot->children.end(), + [](auto c) { return c->type == static_cast<int>(AST::Extended::AST_DOT); }); parent_node = dot; dot = (dot_it != dot->children.end()) ? *dot_it : nullptr; } @@ -1155,7 +1186,7 @@ } // handle vpiBinStrVal, vpiDecStrVal and vpiHexStrVal if (std::strchr(val.value.str, '\'')) { - return VERILOG_FRONTEND::const2ast(val.value.str, 0, false); + return ::systemverilog_plugin::const2ast(val.value.str, 0, false); } else { auto size = vpi_get(vpiSize, obj_h); if (size == 0) { @@ -1163,7 +1194,7 @@ c->is_unsized = true; return c; } else { - return VERILOG_FRONTEND::const2ast(std::to_string(size) + strValType + val.value.str, 0, false); + return ::systemverilog_plugin::const2ast(std::to_string(size) + strValType + val.value.str, 0, false); } } } @@ -1233,15 +1264,15 @@ } break; } - case AST::AST_BREAK: - case AST::AST_CONTINUE: { + case AST::Extended::AST_BREAK: + case AST::Extended::AST_CONTINUE: { std::for_each(it, block->children.end(), [](auto *node) { delete node; }); block->children.erase(it, block->children.end()); if (!continue_wire) continue_wire = make_cond_var("$continue"); auto *continue_id = make_identifier(continue_wire->str); block->children.push_back(make_ast_node(AST::AST_ASSIGN_EQ, {continue_id, AST::AstNode::mkconst_int(1, false)})); - if (type == AST::AST_BREAK) { + if (type == AST::Extended::AST_BREAK) { if (!break_wire) break_wire = make_cond_var("$break"); auto *break_id = make_identifier(break_wire->str); @@ -1998,42 +2029,67 @@ void UhdmAst::process_enum_typespec() { + // BaseTypespec specifies underlying type of the enum. + // The BaseTypespec has at most one explicit packed dimension (range). + // When base type is not specified in SystemVerilog code, it is assumed to be an int. + // Type of enum items (constants) is the same as the enum type. current_node = make_ast_node(AST::AST_ENUM); + bool has_base_type = false; + visit_one_to_one({vpiBaseTypespec}, obj_h, [&](AST::AstNode *node) { + has_base_type = true; + current_node->children = std::move(node->children); + current_node->attributes = std::move(node->attributes); + current_node->is_signed = node->is_signed; + current_node->is_logic = node->is_logic; + delete node; + }); + if (!has_base_type) { + // Base typespec is `int` by default + // TODO (mglb): This is almost the same code as in `process_int_typespec()`. Put common code in dedicated function. + std::vector<AST::AstNode *> packed_ranges; + packed_ranges.push_back(make_range(31, 0)); + add_multirange_wire(current_node, std::move(packed_ranges), {}); + current_node->is_signed = true; + } + // We have to restore node's range_* properties if there's no range. + const auto range_left = current_node->range_left; + const auto range_right = current_node->range_right; + const auto range_valid = current_node->range_valid; + // Create a range from the typespec just for the purpose of copying it to consts. + convert_packed_unpacked_range(current_node); + const auto range_it = std::find_if(current_node->children.cbegin(), current_node->children.cend(), + [](const AST::AstNode *n) { return n->type == AST::AST_RANGE || n->type == AST::AST_MULTIRANGE; }); + const auto *const range = range_it != current_node->children.cend() ? *range_it : nullptr; + if (range) { + current_node->children.erase(range_it); + } else { + current_node->range_left = range_left; + current_node->range_right = range_right; + current_node->range_valid = range_valid; + } + visit_one_to_one({vpiTypedefAlias}, obj_h, [&](AST::AstNode *node) { if (node) { - current_node->attributes["\\enum_base_type"] = node->clone(); + current_node->attributes["\\enum_base_type"] = node; } }); - visit_one_to_many({vpiEnumConst}, obj_h, [&](AST::AstNode *node) { current_node->children.push_back(node); }); - vpiHandle typespec_h = vpi_handle(vpiBaseTypespec, obj_h); - if (typespec_h) { - int typespec_type = vpi_get(vpiType, typespec_h); - switch (typespec_type) { - case vpiLogicTypespec: { - current_node->is_logic = true; - shared.report.mark_handled(typespec_h); - break; + visit_one_to_many({vpiEnumConst}, obj_h, [&](AST::AstNode *node) { + // Enum const must have the same type and ranges as the enum. + node->is_logic = current_node->is_logic; + node->is_signed = current_node->is_signed; + if (range) { + node->children.push_back(range->clone()); + node->range_valid = true; + } else { + node->range_left = range_left; + node->range_right = range_right; + node->range_valid = range_valid; } - case vpiByteTypespec: - case vpiIntTypespec: - case vpiIntegerTypespec: { - current_node->is_signed = vpi_get(vpiSigned, typespec_h); - shared.report.mark_handled(typespec_h); - break; - } - case vpiBitTypespec: { - shared.report.mark_handled(typespec_h); - break; - } - default: { - const uhdm_handle *const handle = (const uhdm_handle *)typespec_h; - const UHDM::BaseClass *const object = (const UHDM::BaseClass *)handle->object; - report_error("%s:%d: Encountered unhandled typespec in process_enum_typespec: '%s' of type '%s'\n", object->VpiFile().c_str(), - object->VpiLineNo(), object->VpiName().c_str(), UHDM::VpiTypeName(typespec_h).c_str()); - break; - } - } - vpi_release_handle(typespec_h); + // IMPORTANT: invalidates `range_it`! + current_node->children.push_back(node); + }); + if (range) { + delete range; } } @@ -2045,7 +2101,6 @@ constant_node->filename = current_node->filename; constant_node->location = current_node->location; current_node->children.push_back(constant_node); - current_node->children.push_back(make_range(constant_node->range_left, constant_node->range_right, true)); } } @@ -3462,7 +3517,7 @@ log_assert(!node->children.empty()); top_node->children.push_back(node->children[0]); } else { - node->type = static_cast<AST::AstNodeType>(AST::AST_DOT); + node->type = static_cast<AST::AstNodeType>(AST::Extended::AST_DOT); top_node->children.push_back(node); top_node = node; } @@ -3525,7 +3580,7 @@ if (vpi_get(vpiType, typespec_h) == vpiStringTypespec) { std::string field_name = vpi_get_str(vpiName, typespec_h); if (field_name != "default") { // TODO: better support of the default keyword - auto field = new AST::AstNode(static_cast<AST::AstNodeType>(AST::AST_DOT)); + auto field = new AST::AstNode(static_cast<AST::AstNodeType>(AST::Extended::AST_DOT)); field->str = field_name; current_node->children[0]->children.push_back(field); } @@ -4315,11 +4370,11 @@ break; case vpiBreak: // Will be resolved later by loop processor - current_node = make_ast_node(static_cast<AST::AstNodeType>(AST::AST_BREAK)); + current_node = make_ast_node(static_cast<AST::AstNodeType>(AST::Extended::AST_BREAK)); break; case vpiContinue: // Will be resolved later by loop processor - current_node = make_ast_node(static_cast<AST::AstNodeType>(AST::AST_CONTINUE)); + current_node = make_ast_node(static_cast<AST::AstNodeType>(AST::Extended::AST_CONTINUE)); break; case vpiGenScopeArray: process_gen_scope_array(); @@ -4472,4 +4527,4 @@ } } -YOSYS_NAMESPACE_END +} // namespace systemverilog_plugin
diff --git a/systemverilog-plugin/UhdmAst.h b/systemverilog-plugin/UhdmAst.h index 1e77c17..f3b1017 100644 --- a/systemverilog-plugin/UhdmAst.h +++ b/systemverilog-plugin/UhdmAst.h
@@ -8,7 +8,8 @@ #include "uhdmastshared.h" #include <uhdm/uhdm.h> -YOSYS_NAMESPACE_BEGIN +namespace systemverilog_plugin +{ class UhdmAst { @@ -16,43 +17,44 @@ // Walks through one-to-many relationships from given parent // node through the VPI interface, visiting child nodes belonging to // ChildrenNodeTypes that are present in the given object. - void visit_one_to_many(const std::vector<int> child_node_types, vpiHandle parent_handle, const std::function<void(AST::AstNode *)> &f); + void visit_one_to_many(const std::vector<int> child_node_types, vpiHandle parent_handle, const std::function<void(::Yosys::AST::AstNode *)> &f); // Walks through one-to-one relationships from given parent // node through the VPI interface, visiting child nodes belonging to // ChildrenNodeTypes that are present in the given object. - void visit_one_to_one(const std::vector<int> child_node_types, vpiHandle parent_handle, const std::function<void(AST::AstNode *)> &f); + void visit_one_to_one(const std::vector<int> child_node_types, vpiHandle parent_handle, const std::function<void(::Yosys::AST::AstNode *)> &f); // Visit children of type vpiRange that belong to the given parent node. - void visit_range(vpiHandle obj_h, const std::function<void(AST::AstNode *)> &f); + void visit_range(vpiHandle obj_h, const std::function<void(::Yosys::AST::AstNode *)> &f); // Visit the default expression assigned to a variable. void visit_default_expr(vpiHandle obj_h); // Create an AstNode of the specified type with metadata extracted from // the given vpiHandle. - AST::AstNode *make_ast_node(AST::AstNodeType type, std::vector<AST::AstNode *> children = {}, bool prefer_full_name = false); + ::Yosys::AST::AstNode *make_ast_node(::Yosys::AST::AstNodeType type, std::vector<::Yosys::AST::AstNode *> children = {}, + bool prefer_full_name = false); // Create an identifier AstNode - AST::AstNode *make_identifier(const std::string &name); + ::Yosys::AST::AstNode *make_identifier(const std::string &name); // Makes the passed node a cell node of the specified type - void make_cell(vpiHandle obj_h, AST::AstNode *node, AST::AstNode *type); + void make_cell(vpiHandle obj_h, ::Yosys::AST::AstNode *node, ::Yosys::AST::AstNode *type); // Moves a type node to the specified node - void move_type_to_new_typedef(AST::AstNode *current_node, AST::AstNode *type_node); + void move_type_to_new_typedef(::Yosys::AST::AstNode *current_node, ::Yosys::AST::AstNode *type_node); // Go up the UhdmAst to find a parent node of the specified type - AST::AstNode *find_ancestor(const std::unordered_set<AST::AstNodeType> &types); + ::Yosys::AST::AstNode *find_ancestor(const std::unordered_set<::Yosys::AST::AstNodeType> &types); // Reports that something went wrong with reading the UHDM file void report_error(const char *format, ...) const; // Processes the value connected to the specified node - AST::AstNode *process_value(vpiHandle obj_h); + ::Yosys::AST::AstNode *process_value(vpiHandle obj_h); // Transforms break and continue nodes into structures accepted by the AST frontend - void transform_breaks_continues(AST::AstNode *loop, AST::AstNode *decl_block); + void transform_breaks_continues(::Yosys::AST::AstNode *loop, ::Yosys::AST::AstNode *decl_block); // The parent UhdmAst UhdmAst *parent; @@ -64,7 +66,7 @@ vpiHandle obj_h = 0; // The current Yosys AST node - AST::AstNode *current_node = nullptr; + ::Yosys::AST::AstNode *current_node = nullptr; // Indentation used for debug printing std::string indent; @@ -128,7 +130,7 @@ void process_logic_var(); void process_sys_func_call(); // use for task calls and function calls - void process_tf_call(AST::AstNodeType type); + void process_tf_call(::Yosys::AST::AstNodeType type); void process_immediate_assert(); void process_hier_path(); void process_logic_typespec(); @@ -148,7 +150,7 @@ void process_while(); void process_gate(); void process_primterm(); - void simplify_parameter(AST::AstNode *parameter, AST::AstNode *module_node = nullptr); + void simplify_parameter(::Yosys::AST::AstNode *parameter, ::Yosys::AST::AstNode *module_node = nullptr); void process_unsupported_stmt(const UHDM::BaseClass *object); UhdmAst(UhdmAst *p, UhdmAstShared &s, const std::string &i) : parent(p), shared(s), indent(i) @@ -161,23 +163,19 @@ UhdmAst(UhdmAstShared &s, const std::string &i = "") : UhdmAst(nullptr, s, i) {} // Visits single VPI object and creates proper AST node - AST::AstNode *process_object(vpiHandle obj_h); + ::Yosys::AST::AstNode *process_object(vpiHandle obj_h); // Visits all VPI design objects and returns created ASTs - AST::AstNode *visit_designs(const std::vector<vpiHandle> &designs); + ::Yosys::AST::AstNode *visit_designs(const std::vector<vpiHandle> &designs); - static const IdString &partial(); - static const IdString &packed_ranges(); - static const IdString &unpacked_ranges(); + static const ::Yosys::IdString &partial(); + static const ::Yosys::IdString &packed_ranges(); + static const ::Yosys::IdString &unpacked_ranges(); // set this attribute to force conversion of multirange wire to single range. It is useful to force-convert some memories. - static const IdString &force_convert(); - static const IdString &is_imported(); + static const ::Yosys::IdString &force_convert(); + static const ::Yosys::IdString &is_imported(); }; -namespace VERILOG_FRONTEND -{ -extern bool sv_mode; -} -YOSYS_NAMESPACE_END +} // namespace systemverilog_plugin #endif
diff --git a/systemverilog-plugin/tests/Makefile b/systemverilog-plugin/tests/Makefile index e078fa7..849a9f9 100644 --- a/systemverilog-plugin/tests/Makefile +++ b/systemverilog-plugin/tests/Makefile
@@ -16,10 +16,18 @@ TESTS = counter \ break_continue \ - separate-compilation + separate-compilation \ + debug-flag \ + report-flag \ + defines \ + defaults include $(shell pwd)/../../Makefile_test.common counter_verify = true break_continue_verify = $(call diff_test,break_continue,out) separate-compilation_verify = true +debug-flag_verify = true +report-flag_verify = true +defaults_verify = true +defines_verify = true
diff --git a/systemverilog-plugin/tests/debug-flag/debug-flag-buf.sv b/systemverilog-plugin/tests/debug-flag/debug-flag-buf.sv new file mode 100644 index 0000000..565946b --- /dev/null +++ b/systemverilog-plugin/tests/debug-flag/debug-flag-buf.sv
@@ -0,0 +1,21 @@ +// 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 BUF ( + input I, + output O +); + assign O = I; +endmodule;
diff --git a/systemverilog-plugin/tests/debug-flag/debug-flag-pkg.sv b/systemverilog-plugin/tests/debug-flag/debug-flag-pkg.sv new file mode 100644 index 0000000..b0362fc --- /dev/null +++ b/systemverilog-plugin/tests/debug-flag/debug-flag-pkg.sv
@@ -0,0 +1,19 @@ +// 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 +package pkg; + parameter BITS = 4; + parameter LOG2DELAY = 22; +endpackage
diff --git a/systemverilog-plugin/tests/debug-flag/debug-flag.tcl b/systemverilog-plugin/tests/debug-flag/debug-flag.tcl new file mode 100644 index 0000000..86cfee5 --- /dev/null +++ b/systemverilog-plugin/tests/debug-flag/debug-flag.tcl
@@ -0,0 +1,16 @@ +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) +} + +# Testing simple round-trip +read_systemverilog -debug -odir $TMP_DIR/debug-flag-test -defer $::env(DESIGN_TOP)-pkg.sv +read_systemverilog -debug -odir $TMP_DIR/debug-flag-test -defer $::env(DESIGN_TOP)-buf.sv +read_systemverilog -debug -odir $TMP_DIR/debug-flag-test -defer $::env(DESIGN_TOP).v +read_systemverilog -debug -odir $TMP_DIR/debug-flag-test -link +hierarchy +write_verilog
diff --git a/systemverilog-plugin/tests/debug-flag/debug-flag.v b/systemverilog-plugin/tests/debug-flag/debug-flag.v new file mode 100644 index 0000000..5bd294a --- /dev/null +++ b/systemverilog-plugin/tests/debug-flag/debug-flag.v
@@ -0,0 +1,31 @@ +// 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 [3:0] led +); + + wire bufg; + BUF bufgctrl ( + .I(clk), + .O(bufg) + ); + reg [pkg::BITS + pkg::LOG2DELAY-1 : 0] counter = 0; + always @(posedge bufg) begin + counter <= counter + 1; + end + assign led[3:0] = counter >> pkg::LOG2DELAY; +endmodule
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/tests/report-flag/report-flag-buf.sv b/systemverilog-plugin/tests/report-flag/report-flag-buf.sv new file mode 100644 index 0000000..565946b --- /dev/null +++ b/systemverilog-plugin/tests/report-flag/report-flag-buf.sv
@@ -0,0 +1,21 @@ +// 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 BUF ( + input I, + output O +); + assign O = I; +endmodule;
diff --git a/systemverilog-plugin/tests/report-flag/report-flag-pkg.sv b/systemverilog-plugin/tests/report-flag/report-flag-pkg.sv new file mode 100644 index 0000000..b0362fc --- /dev/null +++ b/systemverilog-plugin/tests/report-flag/report-flag-pkg.sv
@@ -0,0 +1,19 @@ +// 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 +package pkg; + parameter BITS = 4; + parameter LOG2DELAY = 22; +endpackage
diff --git a/systemverilog-plugin/tests/report-flag/report-flag.tcl b/systemverilog-plugin/tests/report-flag/report-flag.tcl new file mode 100644 index 0000000..3a3cc6d --- /dev/null +++ b/systemverilog-plugin/tests/report-flag/report-flag.tcl
@@ -0,0 +1,16 @@ +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) +} + +# Testing simple round-trip +read_systemverilog -report $TMP_DIR/report-flag-test -odir $TMP_DIR/report-flag-test -defer $::env(DESIGN_TOP)-pkg.sv +read_systemverilog -report $TMP_DIR/report-flag-test -odir $TMP_DIR/report-flag-test -defer $::env(DESIGN_TOP)-buf.sv +read_systemverilog -report $TMP_DIR/report-flag-test -odir $TMP_DIR/report-flag-test -defer $::env(DESIGN_TOP).v +read_systemverilog -report $TMP_DIR/report-flag-test -odir $TMP_DIR/report-flag-test -link +hierarchy +write_verilog
diff --git a/systemverilog-plugin/tests/report-flag/report-flag.v b/systemverilog-plugin/tests/report-flag/report-flag.v new file mode 100644 index 0000000..5bd294a --- /dev/null +++ b/systemverilog-plugin/tests/report-flag/report-flag.v
@@ -0,0 +1,31 @@ +// 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 [3:0] led +); + + wire bufg; + BUF bufgctrl ( + .I(clk), + .O(bufg) + ); + reg [pkg::BITS + pkg::LOG2DELAY-1 : 0] counter = 0; + always @(posedge bufg) begin + counter <= counter + 1; + end + assign led[3:0] = counter >> pkg::LOG2DELAY; +endmodule
diff --git a/systemverilog-plugin/third_party/yosys/README b/systemverilog-plugin/third_party/yosys/README new file mode 100644 index 0000000..ceb62a9 --- /dev/null +++ b/systemverilog-plugin/third_party/yosys/README
@@ -0,0 +1,17 @@ +Files in this directory were copied from Yosys sources and slightly adapted. +Original sources and their license available at https://github.com/YosysHQ/yosys. + +Copied files, their sources, changes & notes: + +- const2ast.cc: yosys/frontends/verilog/const2ast.cc (rev. 72787f5) + - The file is a part of Yosys Verilog frontend, which is not publicly exposed + by Yosys. Copy has been made to avoid relying on internal details. + - Changes: + - C++ includes adapted to not rely on `verilog_frontend.h` file. + - Removed Yosys namespace; `const2ast()` has been placed inside + `systemverilog_plugin` namespace to avoid conflicts with the symbol from + Yosys when statically linking. + +Non-copied files placed here for interfacing purposes: + +- const2ast.h
diff --git a/systemverilog-plugin/third_party/yosys/const2ast.cc b/systemverilog-plugin/third_party/yosys/const2ast.cc new file mode 100644 index 0000000..96ea6cc --- /dev/null +++ b/systemverilog-plugin/third_party/yosys/const2ast.cc
@@ -0,0 +1,252 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com> + * + * 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. + * + * --- + * + * The Verilog frontend. + * + * This frontend is using the AST frontend library (see frontends/ast/). + * Thus this frontend does not generate RTLIL code directly but creates an + * AST directly from the Verilog parse tree and then passes this AST to + * the AST frontend library. + * + * --- + * + * This file contains an ad-hoc parser for Verilog constants. The Verilog + * lexer does only recognize a constant but does not actually split it to its + * components. I.e. it just passes the Verilog code for the constant to the + * bison parser. The parser then uses the function const2ast() from this file + * to create an AST node for the constant. + * + * --- + * + * The file has been adapted for use in Yosys SystemVerilog Plugin. + * + */ + +#include "const2ast.h" +#include "frontends/ast/ast.h" +#include "kernel/log.h" + +#include <string> +#include <cmath> +#include <vector> + +using namespace Yosys; +using namespace Yosys::AST; + +// divide an arbitrary length decimal number by two and return the rest +static int my_decimal_div_by_two(std::vector<uint8_t> &digits) +{ + int carry = 0; + for (size_t i = 0; i < digits.size(); i++) { + if (digits[i] >= 10) + log_file_error(current_filename, get_line_num(), "Invalid use of [a-fxz?] in decimal constant.\n"); + digits[i] += carry * 10; + carry = digits[i] % 2; + digits[i] /= 2; + } + while (!digits.empty() && !digits.front()) + digits.erase(digits.begin()); + return carry; +} + +// find the number of significant bits in a binary number (not including the sign bit) +static int my_ilog2(int x) +{ + int ret = 0; + while (x != 0 && x != -1) { + x = x >> 1; + ret++; + } + return ret; +} + +// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?') +static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized) +{ + // all digits in string (MSB at index 0) + std::vector<uint8_t> digits; + + while (*str) { + if ('0' <= *str && *str <= '9') + digits.push_back(*str - '0'); + else if ('a' <= *str && *str <= 'f') + digits.push_back(10 + *str - 'a'); + else if ('A' <= *str && *str <= 'F') + digits.push_back(10 + *str - 'A'); + else if (*str == 'x' || *str == 'X') + digits.push_back(0xf0); + else if (*str == 'z' || *str == 'Z' || *str == '?') + digits.push_back(0xf1); + str++; + } + + if (base == 10 && GetSize(digits) == 1 && digits.front() >= 0xf0) + base = 2; + + data.clear(); + + if (base == 10) { + while (!digits.empty()) + data.push_back(my_decimal_div_by_two(digits) ? State::S1 : State::S0); + } else { + int bits_per_digit = my_ilog2(base-1); + for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) { + if (*it > (base-1) && *it < 0xf0) + log_file_error(current_filename, get_line_num(), "Digit larger than %d used in in base-%d constant.\n", + base-1, base); + for (int i = 0; i < bits_per_digit; i++) { + int bitmask = 1 << i; + if (*it == 0xf0) + data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx); + else if (*it == 0xf1) + data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz); + else + data.push_back((*it & bitmask) ? State::S1 : State::S0); + } + } + } + + int len = GetSize(data); + RTLIL::State msb = data.empty() ? State::S0 : data.back(); + + if (len_in_bits < 0) { + if (len < 32) + data.resize(32, msb == State::S0 || msb == State::S1 ? RTLIL::S0 : msb); + return; + } + + if (is_unsized && (len > len_in_bits)) + log_file_error(current_filename, get_line_num(), "Unsized constant must have width of 1 bit, but have %d bits!\n", len); + + for (len = len - 1; len >= 0; len--) + if (data[len] == State::S1) + break; + if (msb == State::S0 || msb == State::S1) { + len += 1; + data.resize(len_in_bits, State::S0); + } else { + len += 2; + data.resize(len_in_bits, msb); + } + + if (len_in_bits == 0) + log_file_error(current_filename, get_line_num(), "Illegal integer constant size of zero (IEEE 1800-2012, 5.7).\n"); + + if (len > len_in_bits) + log_warning("Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n", + len_in_bits, len, current_filename.c_str(), get_line_num()); +} + +// convert the Verilog code for a constant to an AST node +AstNode *systemverilog_plugin::const2ast(std::string code, char case_type, bool warn_z) +{ + if (warn_z) { + AstNode *ret = const2ast(code, case_type); + if (ret != nullptr && std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end()) + log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n", + current_filename.c_str(), get_line_num()); + return ret; + } + + const char *str = code.c_str(); + + // Strings + if (*str == '"') { + int len = strlen(str) - 2; + std::vector<RTLIL::State> data; + data.reserve(len * 8); + for (int i = 0; i < len; i++) { + unsigned char ch = str[len - i]; + for (int j = 0; j < 8; j++) { + data.push_back((ch & 1) ? State::S1 : State::S0); + ch = ch >> 1; + } + } + AstNode *ast = AstNode::mkconst_bits(data, false); + ast->str = code; + return ast; + } + + for (size_t i = 0; i < code.size(); i++) + if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n') + code.erase(code.begin()+(i--)); + str = code.c_str(); + + char *endptr; + long len_in_bits = strtol(str, &endptr, 10); + + // Simple base-10 integer + if (*endptr == 0) { + std::vector<RTLIL::State> data; + my_strtobin(data, str, -1, 10, case_type, false); + if (data.back() == State::S1) + data.push_back(State::S0); + return AstNode::mkconst_bits(data, true); + } + + // unsized constant + if (str == endptr) + len_in_bits = -1; + + // The "<bits>'[sS]?[bodhBODH]<digits>" syntax + if (*endptr == '\'') + { + std::vector<RTLIL::State> data; + bool is_signed = false; + bool is_unsized = len_in_bits < 0; + if (*(endptr+1) == 's' || *(endptr+1) == 'S') { + is_signed = true; + endptr++; + } + switch (*(endptr+1)) + { + case 'b': + case 'B': + my_strtobin(data, endptr+2, len_in_bits, 2, case_type, is_unsized); + break; + case 'o': + case 'O': + my_strtobin(data, endptr+2, len_in_bits, 8, case_type, is_unsized); + break; + case 'd': + case 'D': + my_strtobin(data, endptr+2, len_in_bits, 10, case_type, is_unsized); + break; + case 'h': + case 'H': + my_strtobin(data, endptr+2, len_in_bits, 16, case_type, is_unsized); + break; + default: + char next_char = char(tolower(*(endptr+1))); + if (next_char == '0' || next_char == '1' || next_char == 'x' || next_char == 'z') { + is_unsized = true; + my_strtobin(data, endptr+1, 1, 2, case_type, is_unsized); + } else { + return NULL; + } + } + if (len_in_bits < 0) { + if (is_signed && data.back() == State::S1) + data.push_back(State::S0); + } + return AstNode::mkconst_bits(data, is_signed, is_unsized); + } + + return NULL; +}
diff --git a/systemverilog-plugin/third_party/yosys/const2ast.h b/systemverilog-plugin/third_party/yosys/const2ast.h new file mode 100644 index 0000000..f7130a2 --- /dev/null +++ b/systemverilog-plugin/third_party/yosys/const2ast.h
@@ -0,0 +1,13 @@ +#ifndef SYSTEMVERILOG_PLUGIN_CONST2AST_H +#define SYSTEMVERILOG_PLUGIN_CONST2AST_H + +#include "frontends/ast/ast.h" +#include <string> + +namespace systemverilog_plugin +{ + // this function converts a Verilog constant to an AST_CONSTANT node + Yosys::AST::AstNode *const2ast(std::string code, char case_type = 0, bool warn_z = false); +} + +#endif // SYSTEMVERILOG_PLUGIN_CONST2AST_H
diff --git a/systemverilog-plugin/uhdmastfrontend.cc b/systemverilog-plugin/uhdmastfrontend.cc index 0ee97c0..427fb37 100644 --- a/systemverilog-plugin/uhdmastfrontend.cc +++ b/systemverilog-plugin/uhdmastfrontend.cc
@@ -25,7 +25,10 @@ bool shallowVisit = false); } -YOSYS_NAMESPACE_BEGIN +namespace systemverilog_plugin +{ + +using namespace ::Yosys; struct UhdmAstFrontend : public UhdmCommonFrontend { UhdmAstFrontend() : UhdmCommonFrontend("uhdm", "read UHDM file") {} @@ -50,8 +53,8 @@ delete synthSubset; if (this->shared.debug_flag || !this->report_directory.empty()) { for (auto design : restoredDesigns) { - std::stringstream strstr; - UHDM::visit_object(design, 1, "", &this->shared.report.unhandled, this->shared.debug_flag ? std::cout : strstr); + std::ofstream null_stream; + UHDM::visit_object(design, 1, "", &this->shared.report.unhandled, this->shared.debug_flag ? std::cout : null_stream); } } UhdmAst uhdm_ast(this->shared); @@ -68,4 +71,4 @@ void call_log_header(RTLIL::Design *design) override { log_header(design, "Executing UHDM frontend.\n"); } } UhdmAstFrontend; -YOSYS_NAMESPACE_END +} // namespace systemverilog_plugin
diff --git a/systemverilog-plugin/uhdmastreport.cc b/systemverilog-plugin/uhdmastreport.cc index 9a2832f..1df94b3 100644 --- a/systemverilog-plugin/uhdmastreport.cc +++ b/systemverilog-plugin/uhdmastreport.cc
@@ -5,7 +5,10 @@ #include <uhdm/BaseClass.h> #include <unordered_set> -YOSYS_NAMESPACE_BEGIN +namespace systemverilog_plugin +{ + +using namespace ::Yosys; void UhdmAstReport::mark_handled(const UHDM::BaseClass *object) { @@ -84,4 +87,4 @@ index_file << "</body>\n</html>" << std::endl; } -YOSYS_NAMESPACE_END +} // namespace systemverilog_plugin
diff --git a/systemverilog-plugin/uhdmastreport.h b/systemverilog-plugin/uhdmastreport.h index ae16b95..e2403fc 100644 --- a/systemverilog-plugin/uhdmastreport.h +++ b/systemverilog-plugin/uhdmastreport.h
@@ -8,7 +8,8 @@ #undef cover #include <uhdm/uhdm.h> -YOSYS_NAMESPACE_BEGIN +namespace systemverilog_plugin +{ class UhdmAstReport { @@ -30,6 +31,6 @@ void write(const std::string &directory); }; -YOSYS_NAMESPACE_END +} // namespace systemverilog_plugin #endif
diff --git a/systemverilog-plugin/uhdmastshared.h b/systemverilog-plugin/uhdmastshared.h index a1ad1c6..b185975 100644 --- a/systemverilog-plugin/uhdmastshared.h +++ b/systemverilog-plugin/uhdmastshared.h
@@ -5,7 +5,8 @@ #include <string> #include <unordered_map> -YOSYS_NAMESPACE_BEGIN +namespace systemverilog_plugin +{ class UhdmAstShared { @@ -51,19 +52,19 @@ bool link = false; // Top nodes of the design (modules, interfaces) - std::unordered_map<std::string, AST::AstNode *> top_nodes; + std::unordered_map<std::string, ::Yosys::AST::AstNode *> top_nodes; // UHDM node coverage report UhdmAstReport report; // Map from AST param nodes to their types (used for params with struct types) - std::unordered_map<std::string, AST::AstNode *> param_types; + std::unordered_map<std::string, ::Yosys::AST::AstNode *> param_types; - AST::AstNode *current_top_node = nullptr; + ::Yosys::AST::AstNode *current_top_node = nullptr; // Set of non-synthesizable objects to skip in current design; std::set<const UHDM::BaseClass *> nonSynthesizableObjects; }; -YOSYS_NAMESPACE_END +} // namespace systemverilog_plugin #endif
diff --git a/systemverilog-plugin/uhdmcommonfrontend.cc b/systemverilog-plugin/uhdmcommonfrontend.cc index 651552a..167259c 100644 --- a/systemverilog-plugin/uhdmcommonfrontend.cc +++ b/systemverilog-plugin/uhdmcommonfrontend.cc
@@ -19,7 +19,10 @@ #include "uhdmcommonfrontend.h" -YOSYS_NAMESPACE_BEGIN +namespace systemverilog_plugin +{ + +using namespace ::Yosys; /* Stub for AST::process */ static void set_line_num(int) {} @@ -147,4 +150,4 @@ } } -YOSYS_NAMESPACE_END +} // namespace systemverilog_plugin
diff --git a/systemverilog-plugin/uhdmcommonfrontend.h b/systemverilog-plugin/uhdmcommonfrontend.h index e8b59f9..4c10d10 100644 --- a/systemverilog-plugin/uhdmcommonfrontend.h +++ b/systemverilog-plugin/uhdmcommonfrontend.h
@@ -26,7 +26,8 @@ #include <type_traits> #include <vector> -YOSYS_NAMESPACE_BEGIN +namespace systemverilog_plugin +{ // FIXME (mglb): temporary fix to support UHDM both before and after the following change: // https://github.com/chipsalliance/UHDM/commit/d78d094448bd94926644e48adea4df293b82f101 @@ -45,16 +46,16 @@ return new ObjT(std::forward<ArgN>(arg_n)..., true); } -struct UhdmCommonFrontend : public Frontend { +struct UhdmCommonFrontend : public ::Yosys::Frontend { UhdmAstShared shared; std::string report_directory; std::vector<std::string> args; UhdmCommonFrontend(std::string name, std::string short_help) : Frontend(name, short_help) {} virtual void print_read_options(); virtual void help() = 0; - virtual AST::AstNode *parse(std::string filename) = 0; - virtual void call_log_header(RTLIL::Design *design) = 0; - void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design); + virtual ::Yosys::AST::AstNode *parse(std::string filename) = 0; + virtual void call_log_header(::Yosys::RTLIL::Design *design) = 0; + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, ::Yosys::RTLIL::Design *design); }; -YOSYS_NAMESPACE_END +} // namespace systemverilog_plugin
diff --git a/systemverilog-plugin/uhdmsurelogastfrontend.cc b/systemverilog-plugin/uhdmsurelogastfrontend.cc index 4f664c7..a72b405 100644 --- a/systemverilog-plugin/uhdmsurelogastfrontend.cc +++ b/systemverilog-plugin/uhdmsurelogastfrontend.cc
@@ -29,6 +29,9 @@ #include <sys/param.h> #include <unistd.h> #endif +#include <memory> + +#include <list> #include "Surelog/ErrorReporting/Report.h" #include "Surelog/surelog.h" @@ -39,44 +42,78 @@ bool shallowVisit = false); } -YOSYS_NAMESPACE_BEGIN - -std::vector<vpiHandle> executeCompilation(SURELOG::SymbolTable *symbolTable, SURELOG::ErrorContainer *errors, SURELOG::CommandLineParser *clp, - SURELOG::scompiler *compiler) +namespace systemverilog_plugin { - bool success = true; - bool noFatalErrors = true; - unsigned int codedReturn = 0; - clp->setWriteUhdm(false); - errors->printMessages(clp->muteStdout()); - std::vector<vpiHandle> the_design; - if (success && (!clp->help())) { - compiler = SURELOG::start_compiler(clp); - if (!compiler) + +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; - the_design.push_back(SURELOG::get_uhdm_design(compiler)); + if (codedReturn) { + log_error("Error when parsing design. Aborting!\n"); + } + + this->clp = std::move(clp); + this->errors = std::move(errors); + + return this->designs; } - 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"); - } - return the_design; -} + + 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) {} @@ -94,13 +131,33 @@ 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; + } - SURELOG::SymbolTable *symbolTable = new SURELOG::SymbolTable(); - SURELOG::ErrorContainer *errors = new SURELOG::ErrorContainer(symbolTable); - SURELOG::CommandLineParser *clp = new SURELOG::CommandLineParser(errors, symbolTable, false, false); + 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"); @@ -122,25 +179,21 @@ clp->setLink(true); } - SURELOG::scompiler *compiler = nullptr; - const std::vector<vpiHandle> uhdm_design = executeCompilation(symbolTable, errors, clp, compiler); + 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_design) { - std::stringstream strstr; - UHDM::visit_object(design, 1, "", &this->shared.report.unhandled, this->shared.debug_flag ? std::cout : strstr); + 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); } } - SURELOG::shutdown_compiler(compiler); - delete clp; - delete symbolTable; - delete errors; // on parse_only mode, don't try to load design // into yosys if (this->shared.parse_only) return nullptr; - UhdmAst uhdm_ast(this->shared); if (this->shared.defer && !this->shared.link) return nullptr; @@ -151,15 +204,19 @@ UHDM::Serializer serializer; UHDM::SynthSubset *synthSubset = make_new_object_with_optional_extra_true_arg<UHDM::SynthSubset>(&serializer, this->shared.nonSynthesizableObjects, false); - synthSubset->listenDesigns(uhdm_design); + synthSubset->listenDesigns(uhdm_designs); delete synthSubset; } - AST::AstNode *current_ast = uhdm_ast.visit_designs(uhdm_design); + 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"); } @@ -176,7 +233,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; -YOSYS_NAMESPACE_END +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