diff --git a/.github/workflows/build-and-test.sh b/.github/workflows/build-and-test.sh index 69fd8a71a..efab5db15 100755 --- a/.github/workflows/build-and-test.sh +++ b/.github/workflows/build-and-test.sh @@ -28,13 +28,13 @@ fi start_section Building -if [ "$PLUGIN_NAME" == "xdc" ] || [ "$PLUGIN_NAME" == "sdc" ]; then +if [ "$PLUGIN_NAME" == "xdc" ] || [ "$PLUGIN_NAME" == "sdc" ]; then make design_introspection.so -j`nproc` make install_design_introspection -j`nproc` -fi +fi export CXXFLAGS=-Werror -make UHDM_INSTALL_DIR=`pwd`/env/conda/envs/yosys-plugins/ ${PLUGIN_NAME}.so -j`nproc` +make ${PLUGIN_NAME}.so -j`nproc` unset CXXFLAGS end_section diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95737de5f..f2fae724a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,8 +35,6 @@ jobs: - design_introspection - integrateinv - ql-qlf - - systemverilog - - uhdm - dsp-ff steps: diff --git a/.github/workflows/setup.sh b/.github/workflows/setup.sh index 38bcf3ffd..1111aa3a9 100644 --- a/.github/workflows/setup.sh +++ b/.github/workflows/setup.sh @@ -47,9 +47,9 @@ end_section #Install yosys start_section Install-Yosys ( - echo '=================================' - echo 'Making env with Yosys and Surelog' - echo '=================================' + echo '=====================' + echo 'Making env with Yosys' + echo '=====================' make env source env/conda/bin/activate yosys-plugins conda list diff --git a/Makefile b/Makefile index 8fc815dd1..11721f978 100644 --- a/Makefile +++ b/Makefile @@ -22,9 +22,9 @@ # The CI will automatically tag the release with v${PLLUGIN_VERSION} # # TODO: pass as -D to gcc so that modules can provide e.g. --version flags. -PLUGIN_VERSION = 1.20230808 +PLUGIN_VERSION = 1.20230906 -PLUGIN_LIST := fasm xdc params sdc ql-iob design_introspection integrateinv ql-qlf systemverilog uhdm dsp-ff +PLUGIN_LIST := fasm xdc params sdc ql-iob design_introspection integrateinv ql-qlf dsp-ff PLUGINS := $(foreach plugin,$(PLUGIN_LIST),$(plugin).so) PLUGINS_INSTALL := $(foreach plugin,$(PLUGIN_LIST),install_$(plugin)) PLUGINS_CLEAN := $(foreach plugin,$(PLUGIN_LIST),clean_$(plugin)) @@ -87,3 +87,17 @@ 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 + +# Notify that the SystemVerilog plugin has been moved +.PHONY: systemverilog.so +systemverilog.so: + $(error The SystemVerilog plugin has been moved to https://github.com/chipsalliance/systemverilog-plugin.) + +.PHONY: install_systemverilog +install_systemverilog: systemverilog.so + +.PHONY: clean_systemverilog +clean_systemverilog: systemverilog.so + +.PHONY: test_systemverilog +test_systemverilog: systemverilog.so diff --git a/README.md b/README.md index 53d80e5b5..d440eae6d 100644 --- a/README.md +++ b/README.md @@ -99,15 +99,7 @@ The plugin adds the following commands: ## SystemVerilog plugin -Reads SystemVerilog and UHDM files and processes them into yosys AST. - -The plugin adds the following commands: - -* read_systemverilog -* read_uhdm - -Detailed help on the supported command(s) can be obtained by running `help ` in Yosys. - +The SystemVerilog plugin has been moved to [chipsalliance/systemverilog-plugin](https://github.com/chipsalliance/systemverilog-plugin). ## Clock Gating plugin diff --git a/environment.yml b/environment.yml index a4cf46c24..5e5062b3d 100644 --- a/environment.yml +++ b/environment.yml @@ -20,5 +20,4 @@ channels: - litex-hub dependencies: - litex-hub::yosys=0.17_7_g990c9b8e1=20220512_085338_py37 - - litex-hub::surelog - litex-hub::iverilog diff --git a/systemverilog-plugin/Makefile b/systemverilog-plugin/Makefile deleted file mode 100644 index 525d43a9e..000000000 --- a/systemverilog-plugin/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# 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 - -PLUGIN_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) - -NAME = systemverilog -SOURCES = UhdmAst.cc \ - uhdmastfrontend.cc \ - uhdmcommonfrontend.cc \ - uhdmsurelogastfrontend.cc \ - third_party/yosys/const2ast.cc \ - third_party/yosys/simplify.cc - -# Directory to search for Surelog and UHDM libraries -UHDM_INSTALL_DIR ?= /usr/local - -# Tell pkg-config to look in the provided install path first. -# PKG_CONFIG_PATH and PKG_CONFIG_PATH_FOR_TARGET are search paths it looks in -# so set the environment variables and prefix with our local install first -PKG_CONFIG_INVOKE = \ - PKG_CONFIG_PATH=$(UHDM_INSTALL_DIR)/lib/pkgconfig:${PKG_CONFIG_PATH} \ - PKG_CONFIG_PATH_FOR_TARGET=$(UHDM_INSTALL_DIR)/lib/pkgconfig:${PKG_CONFIG_PATH_FOR_TARGET} \ - pkg-config - -include ../Makefile_plugin.common - -# A litmus-test: make compilation fail if pkg-config fails -.SECONDARY: $(BUILD_DIR)/.$(NAME)-deps-test -$(BUILD_DIR)/.$(NAME)-deps-test: - $(PKG_CONFIG_INVOKE) --cflags Surelog - -${SO_LIB}: | $(BUILD_DIR)/.$(NAME)-deps-test - -CXXFLAGS += -std=c++17 -Wall -W -Wextra \ - -Wno-deprecated-declarations \ - -Wno-unused-parameter \ - $(shell $(PKG_CONFIG_INVOKE) --cflags Surelog) - -LDFLAGS += $(shell $(PKG_CONFIG_INVOKE) --libs-only-L Surelog) - -LDLIBS += $(shell $(PKG_CONFIG_INVOKE) --libs-only-l --libs-only-other Surelog) diff --git a/systemverilog-plugin/README.md b/systemverilog-plugin/README.md index cdd2edd48..c67fd2e71 100644 --- a/systemverilog-plugin/README.md +++ b/systemverilog-plugin/README.md @@ -1,117 +1,3 @@ # SystemVerilog Plugin -Reads SystemVerilog and UHDM files and processes them into Yosys AST. - -The plugin adds the following commands: - -* `read_systemverilog` -* `read_uhdm` - -A more detailed help on the supported commands can be obtained by running `help ` in Yosys. - -Please see the dedicated [integration repository](https://github.com/antmicro/yosys-uhdm-plugin-integration) which contains more information about installation and usage of this plugin. -This repository also runs dedicated CI pipelines that perform extensive testing of this plugin. - -## Installation - -A pre-built binary can be downloaded from the [release page](https://github.com/antmicro/yosys-uhdm-plugin-integration/releases). -The release archive contains an installation script that detects Yosys installation and installs the plugin. - -To build from sources please refer to the [integration repository](https://github.com/antmicro/yosys-uhdm-plugin-integration). - -## Usage - -Usage of the plugin is very simple. - -This paragraph describes the synthesis process given the following `counter.sv` file: - -``` -module top ( - input clk, - output [3:0] led -); - localparam BITS = 4; - localparam LOG2DELAY = 22; - - wire bufg; - BUFG bufgctrl ( - .I(clk), - .O(bufg) - ); - reg [BITS+LOG2DELAY-1:0] counter = 0; - always @(posedge bufg) begin - counter <= counter + 1; - end - assign led[3:0] = counter >> LOG2DELAY; -endmodule -``` - -To load the plugin, execute `plugin -i systemverilog`. -Then to load SystemVerilog sources, execute `read_systemverilog`. -The rest of the flow is exactly the same as without the plugin. - -To synthesize the `counter.sv` file: - -``` -yosys> plugin -i systemverilog -yosys> read_systemverilog counter.v -1. Executing Verilog with UHDM frontend. -[INF:CM0023] Creating log file ./slpp_all/surelog.log. -[WRN:PA0205] counter.v:1: No timescale set for "top". -[INF:CP0300] Compilation... -[INF:CP0303] counter.v:1: Compile module "work@top". -(...) -Generating RTLIL representation for module `\top'. - -yosys> synth_xilinx - -2. Executing SYNTH_XILINX pass. - -(...) - -3.50. Printing statistics. - -=== top === - - Number of wires: 10 - Number of wire bits: 167 - Number of public wires: 4 - Number of public wire bits: 32 - Number of memories: 0 - Number of memory bits: 0 - Number of processes: 0 - Number of cells: 40 - BUFG 1 - CARRY4 7 - FDRE 26 - IBUF 1 - INV 1 - OBUF 4 - - Estimated number of LCs: 0 - -3.51. Executing CHECK pass (checking for obvious problems). -Checking module top... -Found and reported 0 problems. - -yosys> write_edif counter.edif - -4. Executing EDIF backend. - -``` -As a result we get a `counter.edif` file that can be further processed to get the bitstream. - -### Parsing multiple files -When parsing multiple files you can either pass them together to the `read_systemverilog` command -or read them one by one using `-defer` flag. In the latter case, you will need to call -`readsystemverilog -link` after processing all files to elaborate them. An example flow would -look like below: -``` -plugin -i systemverilog -# Read each file separately -read_systemverilog -defer dut.sv -read_systemverilog -defer top.sv -# Finish reading files, elaborate the design -read_systemverilog -link -# Continue Yosys flow... -``` +The SystemVerilog plugin has been moved to [chipsalliance/systemverilog-plugin](https://github.com/chipsalliance/systemverilog-plugin). diff --git a/systemverilog-plugin/UhdmAst.cc b/systemverilog-plugin/UhdmAst.cc deleted file mode 100644 index 45240c260..000000000 --- a/systemverilog-plugin/UhdmAst.cc +++ /dev/null @@ -1,5373 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "UhdmAst.h" -#include "frontends/ast/ast.h" -#include "libs/sha1/sha1.h" - -#include "utils/memory.h" - -// UHDM -#include -#include -#include - -#include "third_party/yosys/const2ast.h" -#include "third_party/yosys/simplify.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 - -namespace attr_id -{ -static bool already_initialized = false; -static IdString partial; -static IdString packed_ranges; -static IdString unpacked_ranges; -static IdString force_convert; -static IdString is_imported; -static IdString is_simplified_wire; -static IdString low_high_bound; -static IdString is_type_parameter; -static IdString is_elaborated_module; -}; // namespace attr_id - -// TODO(mglb): use attr_id::* directly everywhere and remove those methods. -/*static*/ const IdString &UhdmAst::partial() { return attr_id::partial; } -/*static*/ const IdString &UhdmAst::packed_ranges() { return attr_id::packed_ranges; } -/*static*/ const IdString &UhdmAst::unpacked_ranges() { return attr_id::unpacked_ranges; } -/*static*/ const IdString &UhdmAst::force_convert() { return attr_id::force_convert; } -/*static*/ const IdString &UhdmAst::is_imported() { return attr_id::is_imported; } -/*static*/ const IdString &UhdmAst::is_simplified_wire() { return attr_id::is_simplified_wire; } -/*static*/ const IdString &UhdmAst::low_high_bound() { return attr_id::low_high_bound; } -/*static*/ const IdString &UhdmAst::is_elaborated_module() { return attr_id::is_elaborated_module; } - -#define MAKE_INTERNAL_ID(X) IdString("$systemverilog_plugin$" #X) - -void attr_id_init() -{ - // Initialize only once - if (attr_id::already_initialized) - return; - attr_id::already_initialized = true; - - // Actual initialization - - // Register IdStrings. Can't be done statically, as the IdString class uses resources created during Yosys initialization which happens after - // static initialization of the plugin when everything is statically linked. - attr_id::partial = MAKE_INTERNAL_ID(partial); - attr_id::packed_ranges = MAKE_INTERNAL_ID(packed_ranges); - attr_id::unpacked_ranges = MAKE_INTERNAL_ID(unpacked_ranges); - attr_id::force_convert = MAKE_INTERNAL_ID(force_convert); - attr_id::is_imported = MAKE_INTERNAL_ID(is_imported); - attr_id::is_simplified_wire = MAKE_INTERNAL_ID(is_simplified_wire); - attr_id::low_high_bound = MAKE_INTERNAL_ID(low_high_bound); - attr_id::is_type_parameter = MAKE_INTERNAL_ID(is_type_parameter); - attr_id::is_elaborated_module = MAKE_INTERNAL_ID(is_elaborated_module); -} - -void attr_id_cleanup() -{ - // Release static copies of private IdStrings. - attr_id::low_high_bound = IdString(); - attr_id::is_simplified_wire = IdString(); - attr_id::is_imported = IdString(); - attr_id::force_convert = IdString(); - attr_id::unpacked_ranges = IdString(); - attr_id::packed_ranges = IdString(); - attr_id::partial = IdString(); - attr_id::is_type_parameter = IdString(); - attr_id::is_elaborated_module = IdString(); - attr_id::already_initialized = false; -} - -static AST::AstNode *get_attribute(AST::AstNode *node, const IdString &attribute) -{ - log_assert(node); - if (!node->attributes.count(attribute)) - return nullptr; - - return node->attributes[attribute]; -} - -// Consumes attr_node. -static void set_attribute(AST::AstNode *node, const IdString &attribute, AST::AstNode *attr_node) -{ - log_assert(node); - log_assert(attr_node); - delete node->attributes[attribute]; - node->attributes[attribute] = attr_node; -} - -// Delete the selected attribute if it exists. -// Does nothing if the node doesn't exist, or the attribute doesn't exists. -static void delete_attribute(AST::AstNode *node, const IdString &attribute) -{ - if (!node) - return; - - if (node->attributes.count(attribute)) { - delete node->attributes[attribute]; - node->attributes.erase(attribute); - } -} - -// Delete all attributes that belong to the SV plugin. -// The attributes beloning to Yosys are *not* deleted here. -static void delete_internal_attributes(AST::AstNode *node) -{ - if (!node) - return; - - for (auto &attr : {UhdmAst::partial(), UhdmAst::packed_ranges(), UhdmAst::unpacked_ranges(), UhdmAst::force_convert(), UhdmAst::is_imported(), - UhdmAst::is_simplified_wire(), UhdmAst::low_high_bound(), attr_id::is_type_parameter, attr_id::is_elaborated_module}) { - delete_attribute(node, attr); - } -} - -template class ScopedValueChanger -{ - T &ref; - const T prev_val; - - public: - ScopedValueChanger() = delete; - - explicit ScopedValueChanger(T &r) : ref(r), prev_val(ref) {} - - ScopedValueChanger(T &r, const T &val) : ref(r), prev_val(ref) { ref = val; } - - ScopedValueChanger(ScopedValueChanger &&) = delete; - ScopedValueChanger &operator=(ScopedValueChanger &&) = delete; - - ScopedValueChanger(const ScopedValueChanger &) = delete; - ScopedValueChanger &operator=(const ScopedValueChanger &) = delete; - - ~ScopedValueChanger() { ref = prev_val; } -}; - -template ScopedValueChanger(T &)->ScopedValueChanger; - -template ScopedValueChanger(T &, const T &)->ScopedValueChanger; - -// Delete all children nodes. -// Does *not* delete attributes. -// This function exists as Yosys's function node->delete_children() does remove all children and attributes. -static void delete_children(AST::AstNode *node) -{ - if (!node) - return; - - for (auto *child : node->children) { - delete child; - } - node->children.clear(); -} - -static void simplify_sv(AST::AstNode *current_node, AST::AstNode *parent_node); - -static void sanitize_symbol_name(std::string &name) -{ - if (!name.empty()) { - auto pos = name.find_last_of('@'); - name = name.substr(pos + 1); - // symbol names must begin with '\' - name.insert(0, "\\"); - } -} - -static std::string get_object_name(vpiHandle obj_h, const std::vector &name_fields = {vpiName}) -{ - std::string objectName; - for (auto name : name_fields) { - if (auto s = vpi_get_str(name, obj_h)) { - objectName = s; - sanitize_symbol_name(objectName); - break; - } - } - return objectName; -} - -static std::string get_name(vpiHandle obj_h) { return get_object_name(obj_h, {vpiName, vpiDefName}); } - -static std::string strip_package_name(std::string name) -{ - auto sep_index = name.find("::"); - if (sep_index != string::npos) { - name = name.substr(sep_index + 1); - name[0] = '\\'; - } - return name; -} - -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. - auto range = new AST::AstNode(AST::AST_RANGE); - range->range_left = left; - range->range_right = right; - range->range_valid = true; - range->children.push_back(AST::AstNode::mkconst_int(left, true)); - range->children.push_back(AST::AstNode::mkconst_int(right, true)); - range->is_signed = is_signed; - return range; -} - -static void copy_packed_unpacked_attribute(AST::AstNode *from, AST::AstNode *to) -{ - if (!to->attributes.count(UhdmAst::packed_ranges())) - to->attributes[UhdmAst::packed_ranges()] = AST::AstNode::mkconst_int(1, false, 1); - if (!to->attributes.count(UhdmAst::unpacked_ranges())) - to->attributes[UhdmAst::unpacked_ranges()] = AST::AstNode::mkconst_int(1, false, 1); - if (from->attributes.count(UhdmAst::packed_ranges())) { - for (auto r : from->attributes[UhdmAst::packed_ranges()]->children) { - to->attributes[UhdmAst::packed_ranges()]->children.push_back(r->clone()); - } - } - if (from->attributes.count(UhdmAst::unpacked_ranges())) { - for (auto r : from->attributes[UhdmAst::unpacked_ranges()]->children) { - to->attributes[UhdmAst::unpacked_ranges()]->children.push_back(r->clone()); - } - } -} - -static int get_max_offset_struct(AST::AstNode *node) -{ - // get the width from the MS member in the struct - // as members are laid out from left to right in the packed wire - log_assert(node->type == AST::AST_STRUCT || node->type == AST::AST_UNION); - while (node->range_left < 0) { - node = node->children[0]; - } - return node->range_left; -} - -static void visitEachDescendant(AST::AstNode *node, const std::function &f) -{ - for (auto child : node->children) { - f(child); - visitEachDescendant(child, f); - } -} - -static void add_multirange_wire(AST::AstNode *node, std::vector packed_ranges, std::vector unpacked_ranges, - bool reverse = true) -{ - delete_attribute(node, UhdmAst::packed_ranges()); - node->attributes[UhdmAst::packed_ranges()] = AST::AstNode::mkconst_int(1, false, 1); - if (!packed_ranges.empty()) { - if (reverse) - std::reverse(packed_ranges.begin(), packed_ranges.end()); - node->attributes[UhdmAst::packed_ranges()]->children = std::move(packed_ranges); - } - - delete_attribute(node, UhdmAst::unpacked_ranges()); - node->attributes[UhdmAst::unpacked_ranges()] = AST::AstNode::mkconst_int(1, false, 1); - if (!unpacked_ranges.empty()) { - if (reverse) - std::reverse(unpacked_ranges.begin(), unpacked_ranges.end()); - node->attributes[UhdmAst::unpacked_ranges()]->children = std::move(unpacked_ranges); - } -} - -// Sets the `wire_node->multirange_dimensions` attribute and returns the total sizes of packed and unpacked ranges. -static std::pair set_multirange_dimensions(AST::AstNode *wire_node, const std::vector packed_ranges, - const std::vector unpacked_ranges) -{ - // node->multirange_dimensions stores dimensions' offsets and widths. - // It shall have even number of elements. - // For a range of [A:B] it should be appended with {min(A,B)} and {max(A,B)-min(A,B)+1} - // For a range of [A] it should be appended with {0} and {A} - - auto calc_range_size = [wire_node](const std::vector &ranges) -> size_t { - size_t size = 1; - for (size_t i = 0; i < ranges.size(); i++) { - log_assert(AST_INTERNAL::current_ast_mod); - simplify_sv(ranges[i], wire_node); - // If it's a range of [A], make it [A:A]. - if (ranges[i]->children.size() == 1) { - ranges[i]->children.push_back(ranges[i]->children[0]->clone()); - } - while (simplify(ranges[i], true, false, false, 1, -1, false, false)) { - } - // this workaround case, where yosys doesn't follow id2ast and simplifies it to resolve constant - if (ranges[i]->children[0]->id2ast) { - simplify_sv(ranges[i]->children[0]->id2ast, ranges[i]->children[0]); - while (simplify(ranges[i]->children[0]->id2ast, true, false, false, 1, -1, false, false)) { - } - } - if (ranges[i]->children[1]->id2ast) { - simplify_sv(ranges[i]->children[1]->id2ast, ranges[i]->children[1]); - while (simplify(ranges[i]->children[1]->id2ast, true, false, false, 1, -1, false, false)) { - } - } - simplify_sv(ranges[i], wire_node); - while (simplify(ranges[i], true, false, false, 1, -1, false, false)) { - } - log_assert(ranges[i]->children[0]->type == AST::AST_CONSTANT); - log_assert(ranges[i]->children[1]->type == AST::AST_CONSTANT); - - const auto low = min(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer); - const auto high = max(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer); - const auto elem_size = high - low + 1; - - wire_node->multirange_dimensions.push_back(low); - wire_node->multirange_dimensions.push_back(elem_size); - wire_node->multirange_swapped.push_back(ranges[i]->range_swapped); - size *= elem_size; - } - return size; - }; - size_t packed_size = calc_range_size(packed_ranges); - size_t unpacked_size = calc_range_size(unpacked_ranges); - log_assert(wire_node->multirange_dimensions.size() % 2 == 0); - return {packed_size, unpacked_size}; -} - -static AST::AstNode *convert_range(AST::AstNode *id, int packed_ranges_size, int unpacked_ranges_size, int i) -{ - log_assert(AST_INTERNAL::current_ast_mod); - log_assert(AST_INTERNAL::current_scope.count(id->str)); - AST::AstNode *wire_node = AST_INTERNAL::current_scope[id->str]; - log_assert(!wire_node->multirange_dimensions.empty()); - int elem_size = 1; - std::vector single_elem_size; - single_elem_size.push_back(elem_size); - for (size_t j = 0; (j + 1) < wire_node->multirange_dimensions.size(); j = j + 2) { - // The ranges' widths are placed on odd indices of multirange_dimensions. - elem_size *= wire_node->multirange_dimensions[j + 1]; - single_elem_size.push_back(elem_size); - } - std::reverse(single_elem_size.begin(), single_elem_size.end()); - log_assert(i < (unpacked_ranges_size + packed_ranges_size)); - log_assert(!id->children.empty()); - AST::AstNode *result = nullptr; - // we want to start converting from the end - if (i < static_cast(id->children.size()) - 1) { - result = convert_range(id, packed_ranges_size, unpacked_ranges_size, i + 1); - } - // special case, we want to select whole wire - if (id->children.size() == 0 && i == 0) { - result = make_range(single_elem_size[i] - 1, 0); - } else { - AST::AstNode *range_left = nullptr; - AST::AstNode *range_right = nullptr; - if (id->children[i]->children.size() == 2) { - range_left = id->children[i]->children[0]->clone(); - range_right = id->children[i]->children[1]->clone(); - } else { - range_left = id->children[i]->children[0]->clone(); - range_right = id->children[i]->children[0]->clone(); - } - if (!wire_node->multirange_swapped.empty()) { - bool is_swapped = wire_node->multirange_swapped[wire_node->multirange_swapped.size() - i - 1]; - auto right_idx = wire_node->multirange_dimensions.size() - (i * 2) - 2; - if (is_swapped) { - auto left_idx = wire_node->multirange_dimensions.size() - (i * 2) - 1; - auto elem_size = wire_node->multirange_dimensions[left_idx] - wire_node->multirange_dimensions[right_idx]; - range_left = new AST::AstNode(AST::AST_SUB, AST::AstNode::mkconst_int(elem_size - 1, false), range_left); - range_right = new AST::AstNode(AST::AST_SUB, AST::AstNode::mkconst_int(elem_size - 1, false), range_right); - } else if (wire_node->multirange_dimensions[right_idx] != 0) { - range_left = - new AST::AstNode(AST::AST_SUB, range_left, AST::AstNode::mkconst_int(wire_node->multirange_dimensions[right_idx], false)); - range_right = - new AST::AstNode(AST::AST_SUB, range_right, AST::AstNode::mkconst_int(wire_node->multirange_dimensions[right_idx], false)); - } - } - if (!result) { - range_left = - new AST::AstNode(AST::AST_SUB, - new AST::AstNode(AST::AST_MUL, new AST::AstNode(AST::AST_ADD, range_left, AST::AstNode::mkconst_int(1, false)), - AST::AstNode::mkconst_int(single_elem_size[i + 1], false)), - AST::AstNode::mkconst_int(1, false)); - } - range_right = new AST::AstNode(AST::AST_MUL, range_right, AST::AstNode::mkconst_int(single_elem_size[i + 1], false)); - if (result) { - range_right = new AST::AstNode(AST::AST_ADD, range_right, result->children[1]->clone()); - delete range_left; - range_left = new AST::AstNode(AST::AST_SUB, new AST::AstNode(AST::AST_ADD, range_right->clone(), result->children[0]->clone()), - result->children[1]->clone()); - delete result; - result = nullptr; - } - result = new AST::AstNode(AST::AST_RANGE, range_left, range_right); - } - // return range from *current* selected range - // in the end, it results in whole selected range - id->basic_prep = true; - return result; -} - -static void resolve_wiretype(AST::AstNode *wire_node) -{ - AST::AstNode *wiretype_node = nullptr; - if (!wire_node->children.empty()) { - if (wire_node->children[0]->type == AST::AST_WIRETYPE) { - wiretype_node = wire_node->children[0]; - } - } - if (wire_node->children.size() > 1) { - if (wire_node->children[1]->type == AST::AST_WIRETYPE) { - wiretype_node = wire_node->children[1]; - } - } - if (wiretype_node == nullptr) - return; - - unique_resource> packed_ranges = wire_node->attributes.count(attr_id::packed_ranges) - ? std::move(wire_node->attributes[attr_id::packed_ranges]->children) - : std::vector{}; - delete_attribute(wire_node, attr_id::packed_ranges); - unique_resource> unpacked_ranges = wire_node->attributes.count(attr_id::unpacked_ranges) - ? std::move(wire_node->attributes[attr_id::unpacked_ranges]->children) - : std::vector{}; - delete_attribute(wire_node, attr_id::unpacked_ranges); - - AST::AstNode *wiretype_ast = nullptr; - log_assert(AST_INTERNAL::current_scope.count(wiretype_node->str)); - wiretype_ast = AST_INTERNAL::current_scope[wiretype_node->str]; - // we need to setup current top ast as this simplify - // needs to have access to all already defined ids - simplify_sv(wiretype_ast, nullptr); - while (simplify(wire_node, true, false, false, 1, -1, false, false)) { - } - log_assert(!wiretype_ast->children.empty()); - if ((wiretype_ast->children[0]->type == AST::AST_STRUCT || wiretype_ast->children[0]->type == AST::AST_UNION) && - wire_node->type == AST::AST_WIRE) { - auto struct_width = get_max_offset_struct(wiretype_ast->children[0]); - wire_node->range_left = struct_width; - wire_node->children[0]->range_left = struct_width; - wire_node->children[0]->children[0]->integer = struct_width; - } - if (wiretype_ast) { - log_assert(wire_node->attributes.count(ID::wiretype)); - log_assert(wiretype_ast->type == AST::AST_TYPEDEF); - wire_node->attributes[ID::wiretype]->id2ast = wiretype_ast->children[0]; - } - if (((wire_node->children.size() > 0 && wire_node->children[0]->type == AST::AST_RANGE) || - (wire_node->children.size() > 1 && wire_node->children[1]->type == AST::AST_RANGE)) && - wire_node->multirange_dimensions.empty()) { - // We need to save order in which ranges appear in wiretype and add them before wire range - // We need to copy this ranges, so create new vector for them - std::vector packed_ranges_wiretype; - std::vector unpacked_ranges_wiretype; - if (wiretype_ast && !wiretype_ast->children.empty() && wiretype_ast->children[0]->attributes.count(UhdmAst::packed_ranges()) && - wiretype_ast->children[0]->attributes.count(UhdmAst::unpacked_ranges())) { - for (auto r : wiretype_ast->children[0]->attributes[UhdmAst::packed_ranges()]->children) { - packed_ranges_wiretype.push_back(r->clone()); - } - for (auto r : wiretype_ast->children[0]->attributes[UhdmAst::unpacked_ranges()]->children) { - unpacked_ranges_wiretype.push_back(r->clone()); - } - } else { - if (wire_node->children[0]->type == AST::AST_RANGE) - packed_ranges_wiretype.push_back(wire_node->children[0]->clone()); - else if (wire_node->children[1]->type == AST::AST_RANGE) - packed_ranges_wiretype.push_back(wire_node->children[1]->clone()); - else - log_error("Unhandled case in resolve_wiretype!\n"); - } - // add wiretype range before current wire ranges - std::reverse(packed_ranges_wiretype.begin(), packed_ranges_wiretype.end()); - std::reverse(unpacked_ranges_wiretype.begin(), unpacked_ranges_wiretype.end()); - std::reverse(packed_ranges->begin(), packed_ranges->end()); - std::reverse(unpacked_ranges->begin(), unpacked_ranges->end()); - packed_ranges->insert(packed_ranges->begin(), packed_ranges_wiretype.begin(), packed_ranges_wiretype.end()); - unpacked_ranges->insert(unpacked_ranges->begin(), unpacked_ranges_wiretype.begin(), unpacked_ranges_wiretype.end()); - AST::AstNode *value = nullptr; - if (wire_node->children[0]->type != AST::AST_RANGE) { - value = wire_node->children[0]->clone(); - } - delete_children(wire_node); - if (value) - wire_node->children.push_back(value); - add_multirange_wire(wire_node, packed_ranges.release(), unpacked_ranges.release(), false /* reverse */); - } -} - -static void add_force_convert_attribute(AST::AstNode *wire_node, uint32_t val = 1) -{ - AST::AstNode *&attr = wire_node->attributes[UhdmAst::force_convert()]; - if (!attr) { - attr = AST::AstNode::mkconst_int(val, true); - } else if (attr->integer != val) { - attr->integer = val; - } -} - -static void check_memories(AST::AstNode *node, std::string scope, std::map &memories) -{ - for (auto *child : node->children) { - check_memories(child, node->type == AST::AST_GENBLOCK ? scope + "." + node->str : scope, memories); - } - - if (node->str == "\\$readmemh") { - if (node->children.size() != 2 || node->children[1]->str.empty() || node->children[1]->type != AST::AST_IDENTIFIER) { - log_error("%s:%d: Wrong usage of '\\$readmemh'\n", node->filename.c_str(), node->location.first_line); - } - // TODO: Look for the memory in all other scope levels, like we do in case of AST::AST_IDENTIFIER, - // as here the memory can also be defined before before the current scope. - std::string name = scope + "." + node->children[1]->str; - const auto iter = memories.find(name); - if (iter != memories.end()) { - add_force_convert_attribute(iter->second, 0); - } - } - - if (node->type == AST::AST_WIRE) { - const std::size_t packed_ranges_count = - node->attributes.count(UhdmAst::packed_ranges()) ? node->attributes[UhdmAst::packed_ranges()]->children.size() : 0; - const std::size_t unpacked_ranges_count = - node->attributes.count(UhdmAst::unpacked_ranges()) ? node->attributes[UhdmAst::unpacked_ranges()]->children.size() : 0; - - if (packed_ranges_count == 1 && unpacked_ranges_count == 1) { - std::string name = scope + "." + node->str; - auto [iter, did_insert] = memories.insert_or_assign(std::move(name), node); - log_assert(did_insert); - } - return; - } - - if (node->type == AST::AST_IDENTIFIER) { - std::string full_id = scope; - std::size_t scope_end_pos = scope.size(); - - for (;;) { - full_id += "." + node->str; - const auto iter = memories.find(full_id); - if (iter != memories.end()) { - // Memory node found! - if (!iter->second->attributes.count(UhdmAst::force_convert())) { - const bool is_full_memory_access = (node->children.size() == 0); - const bool is_slice_memory_access = (node->children.size() == 1 && node->children[0]->children.size() != 1); - // convert memory to list of registers - // in case of access to whole memory - // or slice of memory - // e.g. - // logic [3:0] mem [8:0]; - // always_ff @ (posedge clk) begin - // mem <= '{default:0}; - // mem[7:1] <= mem[6:0]; - // end - // don't convert in case of accessing - // memory using address, e.g. - // mem[0] <= '{default:0} - if (is_full_memory_access || is_slice_memory_access) { - add_force_convert_attribute(iter->second); - } - } - break; - } else { - if (scope_end_pos == 0) { - // We reached the top scope and the memory node wasn't found. - break; - } else { - // Memory node wasn't found. - // Erase node name and last segment of the scope to check the previous scope. - // FIXME: This doesn't work with escaped identifiers containing a dot. - scope_end_pos = full_id.find_last_of('.', scope_end_pos - 1); - if (scope_end_pos == std::string::npos) { - scope_end_pos = 0; - } - full_id.erase(scope_end_pos); - } - } - } - } -} - -static void check_memories(AST::AstNode *node) -{ - std::map memories; - check_memories(node, "", memories); -} - -static void warn_start_range(const std::vector ranges) -{ - for (size_t i = 0; i < ranges.size(); i++) { - auto start_elem = min(ranges[i]->children[0]->integer, ranges[i]->children[1]->integer); - if (start_elem != 0) { - log_file_warning(ranges[i]->filename, ranges[i]->location.first_line, "Limited support for multirange wires that don't start from 0\n"); - } - } -} - -// This function is workaround missing support for multirange (with n-ranges) packed/unpacked nodes -// It converts multirange node to single-range node and translates access to this node -// to correct range -static void convert_packed_unpacked_range(AST::AstNode *wire_node) -{ - resolve_wiretype(wire_node); - const std::vector packed_ranges = wire_node->attributes.count(UhdmAst::packed_ranges()) - ? wire_node->attributes[UhdmAst::packed_ranges()]->children - : std::vector(); - const std::vector unpacked_ranges = wire_node->attributes.count(UhdmAst::unpacked_ranges()) - ? wire_node->attributes[UhdmAst::unpacked_ranges()]->children - : std::vector(); - if (packed_ranges.empty() && unpacked_ranges.empty()) { - delete_attribute(wire_node, UhdmAst::packed_ranges()); - delete_attribute(wire_node, UhdmAst::unpacked_ranges()); - wire_node->range_left = 0; - wire_node->range_right = 0; - wire_node->range_valid = true; - return; - } - std::vector ranges; - - // Convert only when node is not a memory and at least 1 of the ranges has more than 1 range - const bool convert_node = [&]() { - if (wire_node->type == AST::AST_MEMORY) - return false; - if (packed_ranges.size() > 1) - return true; - if (unpacked_ranges.size() > 1) - return true; - if (wire_node->attributes.count(ID::wiretype)) - return true; - if (wire_node->type == AST::AST_PARAMETER) - return true; - if (wire_node->type == AST::AST_LOCALPARAM) - return true; - if ((wire_node->is_input || wire_node->is_output) && (packed_ranges.size() > 0 || unpacked_ranges.size() > 0)) - return true; - if (wire_node->attributes.count(UhdmAst::force_convert()) && wire_node->attributes[UhdmAst::force_convert()]->integer == 1) - return true; - return false; - }(); - if (convert_node) { - // if not already converted - if (wire_node->multirange_dimensions.empty()) { - const auto [packed_size, unpacked_size] = set_multirange_dimensions(wire_node, packed_ranges, unpacked_ranges); - if (packed_ranges.size() == 1 && unpacked_ranges.empty()) { - ranges.push_back(packed_ranges[0]->clone()); - } else if (unpacked_ranges.size() == 1 && packed_ranges.empty()) { - ranges.push_back(unpacked_ranges[0]->clone()); - } else { - // currently we have limited support - // for multirange wires that doesn't start from 0 - warn_start_range(packed_ranges); - warn_start_range(unpacked_ranges); - const size_t size = packed_size * unpacked_size; - log_assert(size >= 1); - ranges.push_back(make_range(size - 1, 0)); - } - } - } else { - for (auto r : packed_ranges) { - ranges.push_back(r->clone()); - } - for (auto r : unpacked_ranges) { - ranges.push_back(r->clone()); - } - // if there is only one packed and one unpacked range, - // and wire is not port wire, change type to AST_MEMORY - if (wire_node->type == AST::AST_WIRE && packed_ranges.size() == 1 && unpacked_ranges.size() == 1 && !wire_node->is_input && - !wire_node->is_output) { - wire_node->type = AST::AST_MEMORY; - wire_node->is_logic = true; - } - } - - // Insert new range - wire_node->children.insert(wire_node->children.end(), ranges.begin(), ranges.end()); -} - -// Assert macro that prints location in C++ code and location of currently processed UHDM object. -// Use only inside UhdmAst methods. -#ifndef NDEBUG -#if __GNUC__ -// gcc/clang's __builtin_trap() makes gdb stop on the line containing an assertion. -#define uhdmast_assert(expr) \ - if ((expr)) { \ - } else { \ - this->uhdmast_assert_log(#expr, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ - __builtin_trap(); \ - } -#else // #if __GNUC__ -// Just abort when using compiler other than gcc/clang. -#define uhdmast_assert(expr) \ - if ((expr)) { \ - } else { \ - this->uhdmast_assert_log(#expr, __func__, __FILE__, __LINE__); \ - std::abort(); \ - } -#endif // #if __GNUC__ -#else // #ifndef NDEBUG -#define uhdmast_assert(expr) \ - if ((expr)) { \ - } else { \ - } -#endif // #ifndef NDEBUG - -void UhdmAst::uhdmast_assert_log(const char *expr_str, const char *func, const char *file, int line) const -{ - std::cerr << file << ':' << line << ": error: Assertion failed: " << expr_str << std::endl; - std::cerr << file << ':' << line << ": note: In function: " << func << std::endl; - if (obj_h != 0) { - const char *const svfile = vpi_get_str(vpiFile, obj_h); - int svline = vpi_get(vpiLineNo, obj_h); - int svcolumn = vpi_get(vpiColumnNo, obj_h); - std::string obj_type_name = UHDM::VpiTypeName(obj_h); - const char *obj_name = vpi_get_str(vpiName, obj_h); - std::cerr << svfile << ':' << svline << ':' << svcolumn << ": note: When processing object of type '" << obj_type_name << '\''; - if (obj_name && obj_name[0] != '\0') { - std::cerr << " named '" << obj_name << '\''; - } - std::cerr << '.' << std::endl; - } -} - -static AST::AstNode *expand_dot(const AST::AstNode *current_struct, const AST::AstNode *search_node) -{ - AST::AstNode *current_struct_elem = nullptr; - auto search_str = search_node->str.find("\\") == 0 ? search_node->str.substr(1) : search_node->str; - auto struct_elem_it = - std::find_if(current_struct->children.begin(), current_struct->children.end(), [&](AST::AstNode *node) { return node->str == search_str; }); - if (struct_elem_it == current_struct->children.end()) { - current_struct->dumpAst(NULL, "struct >"); - log_error("Couldn't find search elem: %s in struct\n", search_str.c_str()); - } - current_struct_elem = *struct_elem_it; - - AST::AstNode *sub_dot = nullptr; - std::vector struct_ranges; - - for (auto c : search_node->children) { - if (c->type == static_cast(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); - } - if (c->type == AST::AST_RANGE) { - struct_ranges.push_back(c); - } - } - AST::AstNode *left = nullptr, *right = nullptr; - switch (current_struct_elem->type) { - case AST::AST_STRUCT_ITEM: - left = AST::AstNode::mkconst_int(current_struct_elem->range_left, true); - right = AST::AstNode::mkconst_int(current_struct_elem->range_right, true); - break; - case AST::AST_STRUCT: - case AST::AST_UNION: - // TODO(krak): add proper support for accessing struct/union elements - // with multirange - // Currently support only special access to 2 dimensional packed element - // when selecting single range - log_assert(current_struct_elem->multirange_dimensions.size() % 2 == 0); - if (!struct_ranges.empty() && (current_struct_elem->multirange_dimensions.size() / 2) == 2) { - // get element size in number of bits - const int single_elem_size = current_struct_elem->children.front()->range_left + 1; - left = AST::AstNode::mkconst_int(single_elem_size * current_struct_elem->multirange_dimensions.back(), true); - right = - AST::AstNode::mkconst_int(current_struct_elem->children.back()->range_right * current_struct_elem->multirange_dimensions.back(), true); - } else { - left = AST::AstNode::mkconst_int(current_struct_elem->children.front()->range_left, true); - right = AST::AstNode::mkconst_int(current_struct_elem->children.back()->range_right, true); - } - break; - default: - // Structs currently can only have AST_STRUCT, AST_STRUCT_ITEM, or AST_UNION. - log_file_error(current_struct_elem->filename, current_struct_elem->location.first_line, - "Accessing struct member of type %s is unsupported.\n", type2str(current_struct_elem->type).c_str()); - }; - - auto elem_size = - new AST::AstNode(AST::AST_ADD, new AST::AstNode(AST::AST_SUB, left->clone(), right->clone()), AST::AstNode::mkconst_int(1, true)); - - if (sub_dot) { - // First select correct element in first struct - std::swap(left, sub_dot->children[0]); - std::swap(right, sub_dot->children[1]); - delete sub_dot; - } - - for (size_t i = 0; i < struct_ranges.size(); i++) { - const auto *struct_range = struct_ranges[i]; - auto const range_width_idx = i * 2 + 1; - auto const range_offset_idx = i * 2; - - int range_width = 0; - if (current_struct_elem->multirange_dimensions.empty()) { - range_width = 1; - } else if (current_struct_elem->multirange_dimensions.size() > range_width_idx) { - range_width = current_struct_elem->multirange_dimensions[range_width_idx]; - const auto range_offset = current_struct_elem->multirange_dimensions[range_offset_idx]; - if (range_offset != 0) { - log_file_error(struct_range->filename, struct_range->location.first_line, - "Accessing ranges that do not start from 0 is not supported."); - } - } else { - struct_range->dumpAst(NULL, "range >"); - log_file_error(struct_range->filename, struct_range->location.first_line, "Couldn't find range width."); - } - // now we have correct element set, - // but we still need to set correct struct - log_assert(!struct_range->children.empty()); - if (current_struct_elem->type == AST::AST_STRUCT_ITEM) { - // if we selecting range of struct item, just add this range - // to our current select - if (current_struct_elem->multirange_dimensions.size() > 2 && struct_range->children.size() == 2) { - log_error("Selecting a range of positions from a multirange is not supported in the dot notation.\n"); - } - if (struct_range->children.size() == 2) { - auto range_size = new AST::AstNode( - AST::AST_ADD, new AST::AstNode(AST::AST_SUB, struct_range->children[0]->clone(), struct_range->children[1]->clone()), - AST::AstNode::mkconst_int(1, true)); - right = new AST::AstNode(AST::AST_ADD, right, struct_range->children[1]->clone()); - delete left; - left = new AST::AstNode(AST::AST_ADD, right->clone(), new AST::AstNode(AST::AST_SUB, range_size, AST::AstNode::mkconst_int(1, true))); - - } else if (struct_range->children.size() == 1) { - // Selected a single position, as in `foo.bar[i]`. - if (range_width > 1 && current_struct_elem->multirange_dimensions.size() > range_width_idx + 2) { - // if it's not the last dimension. - right = new AST::AstNode( - AST::AST_ADD, right, - new AST::AstNode(AST::AST_MUL, struct_range->children[0]->clone(), AST::AstNode::mkconst_int(range_width, true))); - delete left; - left = new AST::AstNode(AST::AST_ADD, right->clone(), AST::AstNode::mkconst_int(range_width - 1, true)); - } else { - right = new AST::AstNode(AST::AST_ADD, right, struct_range->children[0]->clone()); - delete left; - left = right->clone(); - } - } else { - struct_range->dumpAst(NULL, "range >"); - log_error("Unhandled range select (AST_STRUCT_ITEM) in AST_DOT!\n"); - } - } else if (current_struct_elem->type == AST::AST_STRUCT) { - if (struct_range->children.size() == 2) { - right = new AST::AstNode(AST::AST_ADD, right, struct_range->children[1]->clone()); - auto range_size = new AST::AstNode( - AST::AST_ADD, new AST::AstNode(AST::AST_SUB, struct_range->children[0]->clone(), struct_range->children[1]->clone()), - AST::AstNode::mkconst_int(1, true)); - left = new AST::AstNode(AST::AST_ADD, left, new AST::AstNode(AST::AST_SUB, range_size, elem_size->clone())); - } else if (struct_range->children.size() == 1) { - AST::AstNode *mul = new AST::AstNode(AST::AST_MUL, elem_size->clone(), struct_range->children[0]->clone()); - - left = new AST::AstNode(AST::AST_ADD, left, mul); - right = new AST::AstNode(AST::AST_ADD, right, mul->clone()); - } else { - struct_range->dumpAst(NULL, "range >"); - log_error("Unhandled range select (AST_STRUCT) in AST_DOT!\n"); - } - } else { - log_file_error(current_struct_elem->filename, current_struct_elem->location.first_line, - "Accessing member of a slice of type %s is unsupported.\n", type2str(current_struct_elem->type).c_str()); - } - } - delete elem_size; - // Return range from the begining of *current* struct - // When all AST_DOT are expanded it will return range - // from original wire - return new AST::AstNode(AST::AST_RANGE, left, right); -} - -static AST::AstNode *convert_dot(AST::AstNode *wire_node, AST::AstNode *node, AST::AstNode *dot) -{ - AST::AstNode *struct_node = nullptr; - if (wire_node->type == AST::AST_STRUCT || wire_node->type == AST::AST_UNION) { - struct_node = wire_node; - } else if (wire_node->attributes.count(ID::wiretype)) { - log_assert(wire_node->attributes[ID::wiretype]->id2ast); - struct_node = wire_node->attributes[ID::wiretype]->id2ast; - } else { - log_file_error(wire_node->filename, wire_node->location.first_line, "Unsupported node type: %s\n", type2str(wire_node->type).c_str()); - } - log_assert(struct_node); - auto expanded = expand_dot(struct_node, dot); - // Now expand ranges that are at instance part of dotted reference - // `expand_dot` returns AST_RANGE with 2 children that selects member pointed by dotted reference - // now we need to move this range to select correct struct - std::vector struct_ranges; - for (auto c : node->children) { - if (c->type == AST::AST_RANGE) { - struct_ranges.push_back(c); - } - } - log_assert(wire_node->attributes.count(UhdmAst::unpacked_ranges())); - log_assert(wire_node->attributes.count(UhdmAst::packed_ranges())); - log_assert(struct_ranges.size() <= (wire_node->multirange_dimensions.size() / 2)); - const auto wire_node_unpacked_ranges_size = wire_node->attributes[UhdmAst::unpacked_ranges()]->children.size(); - // TODO(krak): wire ranges are sometimes under wiretype node (e.g. in case of typedef) - // but wiretype ranges contains also struct range that is already expanded in 'expand_dot' - // we need to find a way to calculate size of wire ranges without struct range here to enable this assert - // const auto wire_node_packed_ranges_size = wire_node->attributes[UhdmAst::packed_ranges()]->children.size(); - // const auto wire_node_ranges_size = wire_node_packed_ranges_size + wire_node_unpacked_ranges_size; - // log_assert(struct_ranges.size() == (wire_node_ranges_size - 1)); - - // Get size of single structure - int struct_size_int = get_max_offset_struct(struct_node) + 1; - auto wire_dimension_size_it = wire_node->multirange_dimensions.rbegin(); - unsigned long range_id = 0; - for (auto it = struct_ranges.rbegin(); it != struct_ranges.rend(); it++) { - // in 'dot' context, we need to select specific struct element, - // so assert that there is only 1 child in struct range (range with single child) - log_assert((*it)->children.size() == 1); - bool is_unpacked_range = range_id < wire_node_unpacked_ranges_size; - // if unpacked range, select from back - auto elem = is_unpacked_range - ? new AST::AstNode(AST::AST_SUB, AST::AstNode::mkconst_int(*wire_dimension_size_it - 1, true, 32), (*it)->children[0]->clone()) - : (*it)->children[0]->clone(); - // calculate which struct we selected - auto move_offset = new AST::AstNode(AST::AST_MUL, AST::AstNode::mkconst_int(struct_size_int, true, 32), elem); - // move our expanded dot to currently selected struct - expanded->children[0] = new AST::AstNode(AST::AST_ADD, move_offset->clone(), expanded->children[0]); - expanded->children[1] = new AST::AstNode(AST::AST_ADD, move_offset, expanded->children[1]); - struct_size_int *= *wire_dimension_size_it; - // wire_dimension_size stores interleaved offset and size. Move to next dimension's size - wire_dimension_size_it += 2; - range_id++; - } - return expanded; -} - -static void setup_current_scope(std::unordered_map top_nodes, AST::AstNode *current_top_node) -{ - for (auto it = top_nodes.begin(); it != top_nodes.end(); it++) { - if (!it->second) - continue; - if (it->second->type == AST::AST_PACKAGE) { - for (auto &o : it->second->children) { - // import only parameters - if (o->type == AST::AST_TYPEDEF || o->type == AST::AST_PARAMETER || o->type == AST::AST_LOCALPARAM) { - // add imported nodes to current scope - AST_INTERNAL::current_scope[it->second->str + std::string("::") + o->str.substr(1)] = o; - AST_INTERNAL::current_scope[o->str] = o; - } else if (o->type == AST::AST_ENUM) { - AST_INTERNAL::current_scope[o->str] = o; - for (auto c : o->children) { - AST_INTERNAL::current_scope[c->str] = c; - } - } - } - } - } - for (auto &o : current_top_node->children) { - if (o->type == AST::AST_TYPEDEF || o->type == AST::AST_PARAMETER || o->type == AST::AST_LOCALPARAM) { - AST_INTERNAL::current_scope[o->str] = o; - } else if (o->type == AST::AST_ENUM) { - AST_INTERNAL::current_scope[o->str] = o; - for (auto c : o->children) { - AST_INTERNAL::current_scope[c->str] = c; - } - } - } - // hackish way of setting current_ast_mod as it is required - // for simplify to get references for already defined ids - AST_INTERNAL::current_ast_mod = current_top_node; - log_assert(AST_INTERNAL::current_ast_mod != nullptr); -} - -static int range_width_local(AST::AstNode *node, AST::AstNode *rnode) -{ - log_assert(rnode->type == AST::AST_RANGE); - if (!rnode->range_valid) { - log_file_error(node->filename, node->location.first_line, "Size must be constant in packed struct/union member %s\n", node->str.c_str()); - } - // note: range swapping has already been checked for - return rnode->range_left - rnode->range_right + 1; -} - -static void save_struct_array_width_local(AST::AstNode *node, int width) -{ - // stash the stride for the array - node->multirange_dimensions.push_back(width); -} - -static int simplify_struct(AST::AstNode *snode, int base_offset, AST::AstNode *parent_node) -{ - // Struct members will be laid out in the structure contiguously from left to right. - // Union members all have zero offset from the start of the union. - // Determine total packed size and assign offsets. Store these in the member node. - bool is_union = (snode->type == AST::AST_UNION); - int offset = 0; - int packed_width = -1; - for (auto s : snode->children) { - if (s->type == AST::AST_RANGE) { - while (simplify(s, true, false, false, 1, -1, false, false)) { - }; - } - } - // embeded struct or union with range? - auto it = std::remove_if(snode->children.begin(), snode->children.end(), [](AST::AstNode *node) { return node->type == AST::AST_RANGE; }); - std::vector ranges(it, snode->children.end()); - snode->children.erase(it, snode->children.end()); - if (!ranges.empty()) { - for (auto range : ranges) { - snode->multirange_dimensions.push_back(min(range->range_left, range->range_right)); - snode->multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1); - snode->multirange_swapped.push_back(range->range_swapped); - delete range; - } - } - // examine members from last to first - for (auto it = snode->children.rbegin(); it != snode->children.rend(); ++it) { - auto node = *it; - int width; - if (node->type == AST::AST_STRUCT || node->type == AST::AST_UNION) { - // embedded struct or union - width = simplify_struct(node, base_offset + offset, parent_node); - if (!node->multirange_dimensions.empty()) { - // Multiply widths of all dimensions. - // `multirange_dimensions` stores (repeating) pairs of [offset, width]. - for (size_t i = 1; i < node->multirange_dimensions.size(); i += 2) { - width *= node->multirange_dimensions[i]; - } - } - // set range of struct - node->range_right = base_offset + offset; - node->range_left = base_offset + offset + width - 1; - node->range_valid = true; - } else { - log_assert(node->type == AST::AST_STRUCT_ITEM); - if (node->children.size() > 0 && node->children[0]->type == AST::AST_RANGE) { - // member width e.g. bit [7:0] a - width = range_width_local(node, node->children[0]); - if (node->children.size() == 2) { - if (node->children[1]->type == AST::AST_RANGE) { - // unpacked array e.g. bit [63:0] a [0:3] - auto rnode = node->children[1]; - int array_count = range_width_local(node, rnode); - if (array_count == 1) { - // C-type array size e.g. bit [63:0] a [4] - array_count = rnode->range_left; - } - save_struct_array_width_local(node, width); - width *= array_count; - } else { - // array element must be single bit for a packed array - log_file_error(node->filename, node->location.first_line, "Unpacked array in packed struct/union member %s\n", - node->str.c_str()); - } - } - // range nodes are now redundant - for (AST::AstNode *child : node->children) - delete child; - node->children.clear(); - } else if (node->children.size() == 1 && node->children[0]->type == AST::AST_MULTIRANGE) { - // packed 2D array, e.g. bit [3:0][63:0] a - auto rnode = node->children[0]; - if (rnode->children.size() != 2) { - // packed arrays can only be 2D - log_file_error(node->filename, node->location.first_line, "Unpacked array in packed struct/union member %s\n", node->str.c_str()); - } - int array_count = range_width_local(node, rnode->children[0]); - width = range_width_local(node, rnode->children[1]); - save_struct_array_width_local(node, width); - width *= array_count; - // range nodes are now redundant - for (AST::AstNode *child : node->children) - delete child; - node->children.clear(); - } else if (node->range_left < 0) { - // 1 bit signal: bit, logic or reg - width = 1; - } else { - // already resolved and compacted - width = node->range_left - node->range_right + 1; - } - if (is_union) { - node->range_right = base_offset; - node->range_left = base_offset + width - 1; - } else { - node->range_right = base_offset + offset; - node->range_left = base_offset + offset + width - 1; - } - node->range_valid = true; - } - if (is_union) { - // check that all members have the same size - if (packed_width == -1) { - // first member - packed_width = width; - } else { - if (packed_width != width) { - - log_file_error(node->filename, node->location.first_line, "member %s of a packed union has %d bits, expecting %d\n", - node->str.c_str(), width, packed_width); - } - } - } else { - offset += width; - } - } - if (!snode->str.empty() && parent_node && parent_node->type != AST::AST_TYPEDEF && parent_node->type != AST::AST_STRUCT && - AST_INTERNAL::current_scope.count(snode->str) != 0) { - AST_INTERNAL::current_scope[snode->str]->attributes[ID::wiretype] = AST::AstNode::mkconst_str(snode->str); - AST_INTERNAL::current_scope[snode->str]->attributes[ID::wiretype]->id2ast = snode; - } - return (is_union ? packed_width : offset); -} - -static void add_members_to_scope_local(AST::AstNode *snode, std::string name) -{ - // add all the members in a struct or union to local scope - // in case later referenced in assignments - log_assert(snode->type == AST::AST_STRUCT || snode->type == AST::AST_UNION); - for (auto *node : snode->children) { - auto member_name = name + "." + node->str; - AST_INTERNAL::current_scope[member_name] = node; - if (node->type != AST::AST_STRUCT_ITEM) { - // embedded struct or union - add_members_to_scope_local(node, name + "." + node->str); - } - } -} - -static AST::AstNode *make_packed_struct_local(AST::AstNode *template_node, std::string &name) -{ - // create a wire for the packed struct - auto wnode = new AST::AstNode(AST::AST_WIRE); - wnode->str = name; - wnode->is_logic = true; - wnode->range_valid = true; - wnode->is_signed = template_node->is_signed; - int offset = get_max_offset_struct(template_node); - auto range = make_range(offset, 0); - copy_packed_unpacked_attribute(template_node, wnode); - wnode->attributes[UhdmAst::packed_ranges()]->children.insert(wnode->attributes[UhdmAst::packed_ranges()]->children.begin(), range); - // make sure this node is the one in scope for this name - AST_INTERNAL::current_scope[name] = wnode; - // add all the struct members to scope under the wire's name - add_members_to_scope_local(template_node, name); - return wnode; -} - -static void simplify_format_string(AST::AstNode *current_node) -{ - std::string sformat = current_node->children[0]->str; - std::string preformatted_string = ""; - int next_arg = 1; - for (size_t i = 0; i < sformat.length(); i++) { - if (sformat[i] == '%') { - AST::AstNode *node_arg = current_node->children[next_arg]; - char cformat = sformat[++i]; - if (cformat == 'b' or cformat == 'B') { - simplify(node_arg, true, false, false, 1, -1, false, false); - if (node_arg->type != AST::AST_CONSTANT) - log_file_error(current_node->filename, current_node->location.first_line, - "Failed to evaluate system task `%s' with non-constant argument.\n", current_node->str.c_str()); - - RTLIL::Const val = node_arg->bitsAsConst(); - for (int j = val.size() - 1; j >= 0; j--) { - // We add ACII value of 0 to convert number to character - preformatted_string += ('0' + val[j]); - } - delete current_node->children[next_arg]; - current_node->children.erase(current_node->children.begin() + next_arg); - } else { - next_arg++; - preformatted_string += std::string("%") + cformat; - } - } else { - preformatted_string += sformat[i]; - } - } - delete current_node->children[0]; - current_node->children[0] = AST::AstNode::mkconst_str(preformatted_string); -} - -// A wrapper for Yosys simplify function. -// Simplifies AST constructs specific to this plugin to a form understandable by Yosys' simplify and then calls the latter if necessary. -// Since simplify from Yosys has been forked to this codebase, all new code should be added there instead. -static void simplify_sv(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(AST::Extended::AST_DOT); }); - AST::AstNode *dot = (dot_it != current_node->children.end()) ? *dot_it : nullptr; - - AST::AstNode *expanded = nullptr; - if (dot) { - if (!AST_INTERNAL::current_scope.count(current_node->str)) { - // for accessing elements currently unsupported with AST_DOT - // fallback to "." notation - AST::AstNode *prefix_node = nullptr; - AST::AstNode *parent_node = current_node; - while (dot && !dot->str.empty()) { - // it is not possible for AST_RANGE to be after AST::DOT (see process_hier_path function) - if (parent_node->children[0]->type == AST::AST_RANGE) { - if (parent_node->children[1]->type == AST::AST_RANGE) - log_error("Multirange in AST_DOT is currently unsupported\n"); - - dot->type = AST::AST_IDENTIFIER; - simplify_sv(dot, nullptr); - AST::AstNode *range_const = parent_node->children[0]->children[0]; - prefix_node = new AST::AstNode(AST::AST_PREFIX, range_const->clone(), dot->clone()); - 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(AST::Extended::AST_DOT); }); - parent_node = dot; - dot = (dot_it != dot->children.end()) ? *dot_it : nullptr; - } - } - delete_children(current_node); - if (prefix_node != nullptr) { - current_node->type = AST::AST_PREFIX; - current_node->children = prefix_node->children; - - prefix_node->children.clear(); - delete prefix_node; - } - } else { - auto wire_node = AST_INTERNAL::current_scope[current_node->str]; - // make sure wire_node is already simplified - simplify_sv(wire_node, nullptr); - expanded = convert_dot(wire_node, current_node, dot); - } - } - if (expanded) { - delete_children(current_node); - current_node->children.push_back(expanded->clone()); - current_node->basic_prep = true; - delete expanded; - expanded = nullptr; - } - // First simplify children - for (size_t i = 0; i < current_node->children.size(); i++) { - simplify_sv(current_node->children[i], current_node); - } - switch (current_node->type) { - case AST::AST_TYPEDEF: - case AST::AST_ENUM: - case AST::AST_FUNCTION: - AST_INTERNAL::current_scope[current_node->str] = current_node; - break; - case AST::AST_WIRE: - case AST::AST_PARAMETER: - case AST::AST_LOCALPARAM: - if (!current_node->attributes.count(UhdmAst::is_simplified_wire())) { - current_node->attributes[UhdmAst::is_simplified_wire()] = AST::AstNode::mkconst_int(1, true); - AST_INTERNAL::current_scope[current_node->str] = current_node; - convert_packed_unpacked_range(current_node); - } - break; - case AST::AST_IDENTIFIER: - if (!current_node->children.empty() && !current_node->basic_prep) { - log_assert(AST_INTERNAL::current_ast_mod); - if (!AST_INTERNAL::current_scope.count(current_node->str)) { - break; - } - AST::AstNode *wire_node = AST_INTERNAL::current_scope[current_node->str]; - - // if a wire is simplified multiple times, its ranges may be added multiple times and be redundant as a result - if (!wire_node->attributes.count(UhdmAst::is_simplified_wire())) { - simplify_sv(wire_node, nullptr); - } - const int packed_ranges_size = - wire_node->attributes.count(UhdmAst::packed_ranges()) ? wire_node->attributes[UhdmAst::packed_ranges()]->children.size() : 0; - const int unpacked_ranges_size = - wire_node->attributes.count(UhdmAst::unpacked_ranges()) ? wire_node->attributes[UhdmAst::unpacked_ranges()]->children.size() : 0; - if ((wire_node->type == AST::AST_WIRE || wire_node->type == AST::AST_PARAMETER || wire_node->type == AST::AST_LOCALPARAM) && - (packed_ranges_size + unpacked_ranges_size > 1)) { - auto *result = convert_range(current_node, packed_ranges_size, unpacked_ranges_size, 0); - delete_children(current_node); - current_node->children.push_back(result); - } - } - break; - case AST::AST_STRUCT: - case AST::AST_UNION: - if (!current_node->attributes.count(UhdmAst::is_simplified_wire())) { - current_node->attributes[UhdmAst::is_simplified_wire()] = AST::AstNode::mkconst_int(1, true); - simplify_struct(current_node, 0, parent_node); - // instance rather than just a type in a typedef or outer struct? - if (!current_node->str.empty() && current_node->str[0] == '\\') { - // instance so add a wire for the packed structure - auto wnode = make_packed_struct_local(current_node, current_node->str); - convert_packed_unpacked_range(wnode); - log_assert(AST_INTERNAL::current_ast_mod); - AST_INTERNAL::current_ast_mod->children.push_back(wnode); - AST_INTERNAL::current_scope[wnode->str]->attributes[ID::wiretype] = AST::AstNode::mkconst_str(current_node->str); - AST_INTERNAL::current_scope[wnode->str]->attributes[ID::wiretype]->id2ast = current_node; - } - - current_node->basic_prep = true; - } - break; - case AST::AST_STRUCT_ITEM: - if (!current_node->attributes.count(UhdmAst::is_simplified_wire())) { - current_node->attributes[UhdmAst::is_simplified_wire()] = AST::AstNode::mkconst_int(1, true); - AST_INTERNAL::current_scope[current_node->str] = current_node; - convert_packed_unpacked_range(current_node); - while (simplify(current_node, true, false, false, 1, -1, false, false)) { - }; - } - break; - case AST::AST_TCALL: - if (current_node->str == "$display" || current_node->str == "$write") - simplify_format_string(current_node); - break; - case AST::AST_COND: - case AST::AST_CONDX: - case AST::AST_CONDZ: - // handle custom low high bound - if (current_node->attributes.count(UhdmAst::low_high_bound())) { - log_assert(!current_node->children.empty()); - log_assert(current_node->children[0]->type == AST::AST_BLOCK); - log_assert(current_node->children[0]->children.size() == 2); - auto low_high_bound = current_node->children[0]; - // this is executed when condition is met - // save pointer that will be added later again - // as conditions needs to go before this block - auto result = current_node->children[1]; - - current_node->children[0] = nullptr; - current_node->children[1] = nullptr; - delete_children(current_node); - while (simplify(low_high_bound->children[0], true, false, false, 1, -1, false, false)) { - }; - while (simplify(low_high_bound->children[1], true, false, false, 1, -1, false, false)) { - }; - log_assert(low_high_bound->children[0]->type == AST::AST_CONSTANT); - log_assert(low_high_bound->children[1]->type == AST::AST_CONSTANT); - const int low = low_high_bound->children[0]->integer; - const int high = low_high_bound->children[1]->integer; - const int range = low_high_bound->children[1]->range_valid - ? low_high_bound->children[1]->range_left - : low_high_bound->children[0]->range_valid ? low_high_bound->children[0]->range_left : 32; - delete low_high_bound; - // According to standard: - // If the bound to the left of the colon is greater than the - // bound to the right, the range is empty and contains no values. - for (int i = low; i >= low && i <= high; i++) { - current_node->children.push_back(AST::AstNode::mkconst_int(i, false, range)); - } - current_node->children.push_back(result); - delete_attribute(current_node, UhdmAst::low_high_bound()); - } - break; - default: - break; - } -} - -static void clear_current_scope() -{ - // Remove clear current_scope from package nodes - AST_INTERNAL::current_scope.clear(); - // unset current_ast_mod - AST_INTERNAL::current_ast_mod = nullptr; -} - -void UhdmAst::visit_one_to_many(const std::vector child_node_types, vpiHandle parent_handle, const std::function &f) -{ - for (auto child : child_node_types) { - vpiHandle itr = vpi_iterate(child, parent_handle); - while (vpiHandle vpi_child_obj = vpi_scan(itr)) { - UhdmAst uhdm_ast(this, shared, indent + " "); - auto *child_node = uhdm_ast.process_object(vpi_child_obj); - f(child_node); - vpi_release_handle(vpi_child_obj); - } - vpi_release_handle(itr); - } -} - -void UhdmAst::visit_one_to_one(const std::vector child_node_types, vpiHandle parent_handle, const std::function &f) -{ - for (auto child : child_node_types) { - vpiHandle itr = vpi_handle(child, parent_handle); - if (itr) { - UhdmAst uhdm_ast(this, shared, indent + " "); - auto *child_node = uhdm_ast.process_object(itr); - f(child_node); - } - vpi_release_handle(itr); - } -} - -void UhdmAst::visit_range(vpiHandle obj_h, const std::function &f) -{ - std::vector range_nodes; - visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { range_nodes.push_back(node); }); - if (range_nodes.size() > 1) { - auto multirange_node = new AST::AstNode(AST::AST_MULTIRANGE); - multirange_node->children = range_nodes; - f(multirange_node); - } else if (!range_nodes.empty()) { - f(range_nodes[0]); - } -} - -void UhdmAst::visit_default_expr(vpiHandle obj_h) -{ - UhdmAst initial_ast(parent, shared, indent); - UhdmAst block_ast(&initial_ast, shared, indent); - block_ast.visit_one_to_one({vpiExpr}, obj_h, [&](AST::AstNode *expr_node) { - auto mod = find_ancestor({AST::AST_MODULE}); - AST::AstNode *initial_node = nullptr; - AST::AstNode *block_node = nullptr; - auto assign_node = new AST::AstNode(AST::AST_ASSIGN_EQ); - auto id_node = new AST::AstNode(AST::AST_IDENTIFIER); - id_node->str = current_node->str; - - for (auto child : mod->children) { - if (child->type == AST::AST_INITIAL) { - initial_node = child; - break; - } - } - // Ensure single AST_INITIAL node is located in AST_MODULE - // before any AST_ALWAYS - if (initial_node == nullptr) { - initial_node = new AST::AstNode(AST::AST_INITIAL); - auto insert_it = find_if(mod->children.begin(), mod->children.end(), [](AST::AstNode *node) { return (node->type == AST::AST_ALWAYS); }); - mod->children.insert(insert_it, initial_node); - } - // Ensure single AST_BLOCK node in AST_INITIAL - if (!initial_node->children.empty() && initial_node->children[0]) { - block_node = initial_node->children[0]; - } else { - block_node = new AST::AstNode(AST::AST_BLOCK); - initial_node->children.push_back(block_node); - } - auto block_child = - find_if(block_node->children.begin(), block_node->children.end(), [](AST::AstNode *node) { return (node->type == AST::AST_ASSIGN_EQ); }); - // Insert AST_ASSIGN_EQ nodes that came from - // custom_var or int_var before any other AST_ASSIGN_EQ - // Especially before ones explicitly placed in initial block in source code - block_node->children.insert(block_child, assign_node); - assign_node->children.push_back(id_node); - initial_ast.current_node = initial_node; - block_ast.current_node = block_node; - assign_node->children.push_back(expr_node); - }); -} - -AST::AstNode *UhdmAst::process_value(vpiHandle obj_h) -{ - s_vpi_value val; - vpi_get_value(obj_h, &val); - std::string strValType = "'"; - bool is_signed = false; - if (vpiHandle typespec_h = vpi_handle(vpiTypespec, obj_h)) { - is_signed = vpi_get(vpiSigned, typespec_h); - if (is_signed) { - strValType += "s"; - } - vpi_release_handle(typespec_h); - } - std::string val_str; - if (val.format) { // Needed to handle parameter nodes without typespecs and constants - switch (val.format) { - case vpiScalarVal: - return AST::AstNode::mkconst_int(val.value.scalar, false, 1); - case vpiBinStrVal: { - strValType += "b"; - val_str = val.value.str; - break; - } - case vpiDecStrVal: { - strValType += "d"; - val_str = val.value.str; - break; - } - case vpiHexStrVal: { - strValType += "h"; - val_str = val.value.str; - break; - } - case vpiOctStrVal: { - strValType += "o"; - val_str = val.value.str; - break; - } - // Surelog reports constant integers as a unsigned, but by default int is signed - // so we are treating here UInt in the same way as if they would be Int - case vpiUIntVal: - if (val.value.uint > std::numeric_limits::max()) { - // an integer is by default signed, so use 'sd despite the variant vpiUIntVal - strValType = "'sd"; - val_str = std::to_string(val.value.uint); - break; - } - [[fallthrough]]; - case vpiIntVal: { - if (val.value.integer > std::numeric_limits::max()) { - strValType = "'sd"; - val_str = std::to_string(val.value.integer); - break; - } - - auto size = vpi_get(vpiSize, obj_h); - // Surelog by default returns 64 bit numbers and stardard says that they shall be at least 32bits - // yosys is assuming that int/uint is 32 bit, so we are setting here correct size - // NOTE: it *shouldn't* break on explicite 64 bit const values, as they *should* be handled - // above by vpi*StrVal - if (size == 64) { - size = 32; - is_signed = true; - } - auto c = AST::AstNode::mkconst_int(val.format == vpiUIntVal ? val.value.uint : val.value.integer, is_signed, size > 0 ? size : 32); - if (size == 0 || size == -1) - c->is_unsized = true; - return c; - } - case vpiRealVal: - return mkconst_real(val.value.real); - case vpiStringVal: - return AST::AstNode::mkconst_str(val.value.str); - default: { - const uhdm_handle *const handle = (const uhdm_handle *)obj_h; - const UHDM::BaseClass *const object = (const UHDM::BaseClass *)handle->object; - report_error("%.*s:%d: Encountered unhandled constant format %d\n", (int)object->VpiFile().length(), object->VpiFile().data(), - object->VpiLineNo(), val.format); - } - } - // if this constant is under case/casex/casez - // get current case type - char caseType = ' '; - if (vpiHandle caseItem_h = vpi_handle(vpiParent, obj_h)) { - if (vpiHandle case_h = vpi_handle(vpiParent, caseItem_h)) { - switch (vpi_get(vpiCaseType, case_h)) { - case vpiCaseExact: - caseType = ' '; - break; - case vpiCaseX: - caseType = 'x'; - break; - case vpiCaseZ: - caseType = 'z'; - break; - default: { - caseType = ' '; - break; - } - } - vpi_release_handle(case_h); - } - vpi_release_handle(caseItem_h); - } - // handle vpiBinStrVal, vpiDecStrVal and vpiHexStrVal - if (val_str.find('\'') != std::string::npos) { - return ::systemverilog_plugin::const2ast(std::move(val_str), caseType, false); - } else { - auto size = vpi_get(vpiSize, obj_h); - std::string size_str; - if (size > 0) { - size_str = std::to_string(size); - } else if (strValType == "\'b") { - // probably unsized unbased const - // but to make sure parse vpiDecompile - auto decompile = vpi_get_str(vpiDecompile, obj_h); - if (decompile && !std::strchr(decompile, 'b')) { - // unsized unbased - // we can't left size_str empty, as then yosys parses this const as 32bit value - size_str = "1"; - } - } - auto c = ::systemverilog_plugin::const2ast(size_str + strValType + val_str, caseType, false); - if (size <= 0) { - // unsized unbased const - c->is_unsized = true; - } - return c; - } - } - return nullptr; -} - -void UhdmAst::transform_breaks_continues(AST::AstNode *loop, AST::AstNode *decl_block) -{ - AST::AstNode *break_wire = nullptr; - AST::AstNode *continue_wire = nullptr; - // Creates a 1-bit wire with the given name - const auto make_cond_var = [this](const std::string &var_name) { - auto cond_var = - make_ast_node(AST::AST_WIRE, {make_ast_node(AST::AST_RANGE, {AST::AstNode::mkconst_int(0, false), AST::AstNode::mkconst_int(0, false)}), - AST::AstNode::mkconst_int(0, false)}); - cond_var->str = var_name; - cond_var->is_reg = true; - return cond_var; - }; - // Creates a conditional like 'if (!casevar) block' - auto make_case = [this](AST::AstNode *block, const std::string &casevar_name) { - auto *case_node = make_ast_node(AST::AST_CASE); - auto *id = make_identifier(casevar_name); - case_node->children.push_back(id); - auto *constant = AST::AstNode::mkconst_int(0, false, 1); - auto *cond_node = make_ast_node(AST::AST_COND); - cond_node->children.push_back(constant); - cond_node->children.push_back(block); - case_node->children.push_back(cond_node); - return case_node; - }; - // Pre-declare this function to be able to call it recursively - std::function transform_block; - // Transforms the given block if it has a break or continue; recurses into child blocks; return true if a break/continue was encountered - transform_block = [&](AST::AstNode *block) { - auto wrap_and_transform = [&](decltype(block->children)::iterator it) { - // Move the (it, end()) statements into a new block under 'if (!continue) {...}' - auto *new_block = make_ast_node(AST::AST_BLOCK, {it, block->children.end()}); - block->children.erase(it, block->children.end()); - auto *case_node = make_case(new_block, continue_wire->str); - block->children.push_back(case_node); - transform_block(new_block); - }; - - for (auto it = block->children.begin(); it != block->children.end(); it++) { - auto type = static_cast((*it)->type); - switch (type) { - case AST::AST_BLOCK: { - if (transform_block(*it)) { - // If there was a break/continue, we need to wrap the rest of the block in an if - wrap_and_transform(it + 1); - return true; - } - break; - } - case AST::AST_CASE: { - // Go over each block in a case - bool has_jump = false; - for (auto *node : (*it)->children) { - if (node->type == AST::AST_COND) - has_jump = has_jump || transform_block(node->children.back()); - } - if (has_jump) { - // If there was a break/continue, we need to wrap the rest of the block in an if - wrap_and_transform(it + 1); - return true; - } - break; - } - 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::Extended::AST_BREAK) { - if (!break_wire) - break_wire = make_cond_var("$break"); - auto *break_id = make_identifier(break_wire->str); - block->children.push_back(make_ast_node(AST::AST_ASSIGN_EQ, {break_id, AST::AstNode::mkconst_int(1, false)})); - } - return true; - } - } - } - return false; - }; - - // Actual transformation starts here - transform_block(loop->children.back()); - if (continue_wire) { - auto *continue_id = make_identifier(continue_wire->str); - // Reset $continue each iteration - auto *continue_assign = make_ast_node(AST::AST_ASSIGN_EQ, {continue_id, AST::AstNode::mkconst_int(0, false)}); - decl_block->children.insert(decl_block->children.begin(), continue_wire); - loop->children.back()->children.insert(loop->children.back()->children.begin(), continue_assign); - } - if (break_wire) { - auto *break_id = make_identifier(break_wire->str); - // Reset $break before the loop - auto *break_assign = make_ast_node(AST::AST_ASSIGN_EQ, {break_id, AST::AstNode::mkconst_int(0, false)}); - decl_block->children.insert(decl_block->children.begin(), break_assign); - decl_block->children.insert(decl_block->children.begin(), break_wire); - if (loop->type == AST::AST_REPEAT || loop->type == AST::AST_FOR) { - // Wrap loop body in 'if (!break) {...}' - // Changing the for loop condition won't work here, - // as then simplify fails with error "2nd expression of procedural for-loop is not constant!" - auto *case_node = make_case(loop->children.back(), break_wire->str); - auto *new_block = make_ast_node(AST::AST_BLOCK); - new_block->children.push_back(case_node); - new_block->str = loop->children.back()->str; - loop->children.back() = new_block; - } else if (loop->type == AST::AST_WHILE) { - // Add the break var to the loop condition - auto *break_id = make_identifier(break_wire->str); - AST::AstNode *&loop_cond = loop->children[0]; - loop_cond = make_ast_node(AST::AST_LOGIC_AND, {make_ast_node(AST::AST_LOGIC_NOT, {break_id}), loop_cond}); - } else { - log_error("break unsupported for this loop type"); - } - } -} - -void UhdmAst::apply_location_from_current_obj(AST::AstNode &target_node) const -{ - if (auto filename = vpi_get_str(vpiFile, obj_h)) { - target_node.filename = filename; - } - if (unsigned int first_line = vpi_get(vpiLineNo, obj_h)) { - target_node.location.first_line = first_line; - } - if (unsigned int last_line = vpi_get(vpiEndLineNo, obj_h)) { - target_node.location.last_line = last_line; - } else { - target_node.location.last_line = target_node.location.first_line; - } - if (unsigned int first_col = vpi_get(vpiColumnNo, obj_h)) { - target_node.location.first_column = first_col; - } - if (unsigned int last_col = vpi_get(vpiEndColumnNo, obj_h)) { - target_node.location.last_column = last_col; - } else { - target_node.location.last_column = target_node.location.first_column; - } -} - -void UhdmAst::apply_name_from_current_obj(AST::AstNode &target_node) const -{ - target_node.str = get_name(obj_h); - auto it = node_renames.find(target_node.str); - if (it != node_renames.end()) - target_node.str = it->second; -} - -AstNodeBuilder UhdmAst::make_node(AST::AstNodeType type) const -{ - auto node = std::make_unique(type); - apply_location_from_current_obj(*node); - return AstNodeBuilder(std::move(node)); -}; - -AstNodeBuilder UhdmAst::make_named_node(AST::AstNodeType type) const -{ - auto node = std::make_unique(type); - apply_location_from_current_obj(*node); - apply_name_from_current_obj(*node); - return AstNodeBuilder(std::move(node)); -}; - -AstNodeBuilder UhdmAst::make_ident(std::string id) const { return make_node(::Yosys::AST::AST_IDENTIFIER).str(std::move(id)); }; - -AstNodeBuilder UhdmAst::make_const(int32_t value, uint8_t width) const -{ - // Limited to width of the `value` argument. - log_assert(width <= 32); - return make_node(AST::AST_CONSTANT).value(value, true, width); -}; - -AstNodeBuilder UhdmAst::make_const(uint32_t value, uint8_t width) const -{ - // Limited to width of the `value` argument. - log_assert(width <= 32); - return make_node(AST::AST_CONSTANT).value(value, false, width); -}; - -AST::AstNode *UhdmAst::make_ast_node(AST::AstNodeType type, std::vector children) -{ - auto node = new AST::AstNode(type); - apply_name_from_current_obj(*node); - apply_location_from_current_obj(*node); - node->children = children; - return node; -} - -AST::AstNode *UhdmAst::make_identifier(std::string name) -{ - auto *node = make_ast_node(AST::AST_IDENTIFIER); - node->str = std::move(name); - return node; -} - -void UhdmAst::process_packed_array_typespec() -{ - std::vector packed_ranges; - std::vector unpacked_ranges; - current_node = make_ast_node(AST::AST_WIRE); - visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - visit_one_to_one({vpiElemTypespec}, obj_h, [&](AST::AstNode *node) { - if (node && node->type == AST::AST_STRUCT) { - auto str = current_node->str; - // unnamed array of named (struct) array - if (str.empty() && !node->str.empty()) - str = node->str; - node->cloneInto(current_node); - current_node->str = str; - delete node; - } else if (node) { - if (!node->str.empty()) { - AST::AstNode *const wiretype_node = make_named_node(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - current_node->is_custom_type = true; - auto it = shared.param_types.find(wiretype_node->str); - if (it == shared.param_types.end()) - shared.param_types.insert(std::make_pair(wiretype_node->str, node)); - else - delete node; - } else { - delete node; - } - } - }); - add_multirange_wire(current_node, std::move(packed_ranges), std::move(unpacked_ranges)); -} - -static void add_or_replace_child(AST::AstNode *parent, AST::AstNode *child) -{ - if (!child->str.empty()) { - auto it = std::find_if(parent->children.begin(), parent->children.end(), - [child](AST::AstNode *existing_child) { return existing_child->str == child->str; }); - if (it != parent->children.end()) { - // If port direction is already set, copy it to replaced child node - if ((*it)->is_input || (*it)->is_output) { - child->is_input = (*it)->is_input; - child->is_output = (*it)->is_output; - child->port_id = (*it)->port_id; - if (child->type == AST::AST_MEMORY) - child->type = AST::AST_WIRE; - } - child->is_signed = child->is_signed || (*it)->is_signed; - if (!(*it)->children.empty() && child->children.empty()) { - // This is a bit ugly, but if the child we're replacing has children and - // our node doesn't, we copy its children to not lose any information - for (auto grandchild : (*it)->children) { - child->children.push_back(grandchild->clone()); - if (child->type == AST::AST_WIRE && grandchild->type == AST::AST_WIRETYPE) - child->is_custom_type = true; - } - } - if ((*it)->attributes.count(UhdmAst::packed_ranges()) && child->attributes.count(UhdmAst::packed_ranges())) { - if ((!(*it)->attributes[UhdmAst::packed_ranges()]->children.empty() && - child->attributes[UhdmAst::packed_ranges()]->children.empty())) { - - delete_attribute(child, UhdmAst::packed_ranges()); - child->attributes[UhdmAst::packed_ranges()] = (*it)->attributes[UhdmAst::packed_ranges()]->clone(); - } - } - if ((*it)->attributes.count(UhdmAst::unpacked_ranges()) && child->attributes.count(UhdmAst::unpacked_ranges())) { - if ((!(*it)->attributes[UhdmAst::unpacked_ranges()]->children.empty() && - child->attributes[UhdmAst::unpacked_ranges()]->children.empty())) { - - delete_attribute(child, UhdmAst::unpacked_ranges()); - child->attributes[UhdmAst::unpacked_ranges()] = (*it)->attributes[UhdmAst::unpacked_ranges()]->clone(); - } - } - // Surelog doesn't report correct sign value for param_assign nodes - // and only default vpiParameter node have correct sign value, so - // if we are overriding parameter, copy sign value from current node to the new node - if (((*it)->type == AST::AST_PARAMETER || (*it)->type == AST::AST_LOCALPARAM) && child->children.size() && (*it)->children.size()) { - child->children[0]->is_signed = (*it)->children[0]->is_signed; - } - delete *it; - *it = child; - return; - } - parent->children.push_back(child); - } else if (child->type == AST::AST_INITIAL) { - // Special case for initials - // Ensure that there is only one AST_INITIAL in the design - // And there is only one AST_BLOCK inside that initial - // Copy nodes from child initial to parent initial - auto initial_node_it = - find_if(parent->children.begin(), parent->children.end(), [](AST::AstNode *node) { return (node->type == AST::AST_INITIAL); }); - if (initial_node_it != parent->children.end()) { - AST::AstNode *initial_node = *initial_node_it; - - // simplify assumes that initial has a block under it - // In case we don't have one (there were no statements under the initial), let's add it - if (initial_node->children.empty()) { - initial_node->children.push_back(new AST::AstNode(AST::AST_BLOCK)); - } - - log_assert(initial_node->children[0]->type == AST::AST_BLOCK); - log_assert(!(child->children.empty())); - log_assert(child->children[0]->type == AST::AST_BLOCK); - - AST::AstNode *block_node = initial_node->children[0]; - AST::AstNode *child_block_node = child->children[0]; - - // Place the contents of child block node inside parent block - for (auto child_block_child : child_block_node->children) - block_node->children.push_back(child_block_child->clone()); - // Place the remaining contents of child initial node inside the parent initial - for (auto initial_child = child->children.begin() + 1; initial_child != child->children.end(); ++initial_child) { - initial_node->children.push_back((*initial_child)->clone()); - } - delete child; - } else { - // Parent AST_INITIAL does not exist - // Place child AST_INITIAL before AST_ALWAYS if found - auto insert_it = - find_if(parent->children.begin(), parent->children.end(), [](AST::AstNode *node) { return (node->type == AST::AST_ALWAYS); }); - parent->children.insert(insert_it, 1, child); - } - } else { - parent->children.push_back(child); - } -} - -void UhdmAst::make_cell(vpiHandle obj_h, AST::AstNode *cell_node, AST::AstNode *type_node) -{ - if (cell_node->children.empty() || (!cell_node->children.empty() && cell_node->children[0]->type != AST::AST_CELLTYPE)) { - auto typeNode = new AST::AstNode(AST::AST_CELLTYPE); - typeNode->str = type_node->str; - cell_node->children.insert(cell_node->children.begin(), typeNode); - } - // Add port connections as arguments - vpiHandle port_itr = vpi_iterate(vpiPort, obj_h); - while (vpiHandle port_h = vpi_scan(port_itr)) { - std::string arg_name; - if (auto s = vpi_get_str(vpiName, port_h)) { - arg_name = s; - sanitize_symbol_name(arg_name); - } - auto arg_node = new AST::AstNode(AST::AST_ARGUMENT); - arg_node->str = arg_name; - arg_node->filename = cell_node->filename; - arg_node->location = cell_node->location; - visit_one_to_one({vpiHighConn}, port_h, [&](AST::AstNode *node) { - if (node) { - if (node->type == AST::AST_PARAMETER || node->type == AST::AST_LOCALPARAM) { - node->type = AST::AST_IDENTIFIER; - } - arg_node->children.push_back(node); - } - }); - cell_node->children.push_back(arg_node); - vpi_release_handle(port_h); - } - vpi_release_handle(port_itr); -} - -void UhdmAst::move_type_to_new_typedef(AST::AstNode *current_node, AST::AstNode *type_node) -{ - auto typedef_node = new AST::AstNode(AST::AST_TYPEDEF); - typedef_node->location = type_node->location; - typedef_node->filename = type_node->filename; - typedef_node->str = strip_package_name(type_node->str); - for (auto c : current_node->children) { - if (c->str == typedef_node->str) { - return; - } - } - if (type_node->type == AST::AST_STRUCT) { - type_node->str.clear(); - typedef_node->children.push_back(type_node); - current_node->children.push_back(typedef_node); - } else if (type_node->type == AST::AST_ENUM) { - if (type_node->attributes.count("\\enum_base_type")) { - auto base_type = type_node->attributes["\\enum_base_type"]; - auto wire_node = new AST::AstNode(AST::AST_WIRE); - wire_node->is_reg = true; - for (auto c : base_type->children) { - std::string enum_item_str = "\\enum_value_"; - log_assert(!c->children.empty()); - log_assert(c->children[0]->type == AST::AST_CONSTANT); - int width = 1; - bool is_signed = c->children[0]->is_signed; - if (c->children.size() == 2) { - width = c->children[1]->children[0]->integer + 1; - } - RTLIL::Const val = c->children[0]->bitsAsConst(width, is_signed); - enum_item_str.append(val.as_string()); - wire_node->attributes[enum_item_str.c_str()] = AST::AstNode::mkconst_str(c->str); - } - typedef_node->children.push_back(wire_node); - current_node->children.push_back(typedef_node); - delete type_node; - } else { - type_node->str = "$enum" + std::to_string(shared.next_enum_id()); - std::vector packed_ranges; - auto wire_node = new AST::AstNode(AST::AST_WIRE); - wire_node->is_reg = true; - wire_node->attributes["\\enum_type"] = AST::AstNode::mkconst_str(type_node->str); - if (!type_node->children.empty() && type_node->children[0]->children.size() > 1) { - packed_ranges.push_back(type_node->children[0]->children[1]->clone()); - } else { - // Add default range - packed_ranges.push_back(make_range(31, 0)); - } - add_multirange_wire(wire_node, std::move(packed_ranges), {}); - typedef_node->children.push_back(wire_node); - current_node->children.push_back(type_node); - current_node->children.push_back(typedef_node); - } - } else { - type_node->str.clear(); - typedef_node->children.push_back(type_node); - current_node->children.push_back(typedef_node); - } -} - -AST::AstNode *UhdmAst::find_ancestor(const std::unordered_set &types) -{ - auto searched_node = this; - while (searched_node) { - if (searched_node->current_node) { - if (types.find(searched_node->current_node->type) != types.end()) { - return searched_node->current_node; - } - } - searched_node = searched_node->parent; - } - return nullptr; -} - -void UhdmAst::process_design() -{ - current_node = make_ast_node(AST::AST_DESIGN); - visit_one_to_many({UHDM::uhdmallInterfaces, UHDM::uhdmtopPackages, UHDM::uhdmallModules, UHDM::uhdmtopModules, vpiTaskFunc}, obj_h, - [&](AST::AstNode *node) { - if (node) { - shared.top_nodes[node->str] = node; - } - }); - visit_one_to_many({vpiParameter, vpiParamAssign}, obj_h, [&](AST::AstNode *node) { - if (get_attribute(node, attr_id::is_type_parameter)) { - // Don't process type parameters. - delete node; - return; - } - add_or_replace_child(current_node, node); - }); - visit_one_to_many({vpiTypedef}, obj_h, [&](AST::AstNode *node) { - if (node) - move_type_to_new_typedef(current_node, node); - }); - // Add top level typedefs and params to scope - setup_current_scope(shared.top_nodes, current_node); - for (auto pair : shared.top_nodes) { - if (!pair.second) - continue; - if (pair.second->type == AST::AST_PACKAGE) { - check_memories(pair.second); - clear_current_scope(); - setup_current_scope(shared.top_nodes, pair.second); - simplify_sv(pair.second, nullptr); - clear_current_scope(); - } - } - setup_current_scope(shared.top_nodes, current_node); - // Once we walked everything, unroll that as children of this node - for (auto &pair : shared.top_nodes) { - if (!pair.second) - continue; - if (!pair.second->get_bool_attribute(UhdmAst::partial())) { - if (pair.second->type == AST::AST_PACKAGE) - current_node->children.insert(current_node->children.begin(), pair.second); - else { - check_memories(pair.second); - setup_current_scope(shared.top_nodes, pair.second); - simplify_sv(pair.second, nullptr); - clear_current_scope(); - current_node->children.push_back(pair.second); - } - } else { - log_warning("Removing unelaborated module: %s from the design.\n", pair.second->str.c_str()); - // TODO: This should be properly erased from the module, but it seems that it's - // needed to resolve scope - delete pair.second; - pair.second = nullptr; - } - } -} - -void UhdmAst::simplify_parameter(AST::AstNode *parameter, AST::AstNode *module_node) -{ - setup_current_scope(shared.top_nodes, shared.current_top_node); - visitEachDescendant(shared.current_top_node, [&](AST::AstNode *current_scope_node) { - if (current_scope_node->type == AST::AST_TYPEDEF || current_scope_node->type == AST::AST_PARAMETER || - current_scope_node->type == AST::AST_LOCALPARAM) { - AST_INTERNAL::current_scope[current_scope_node->str] = current_scope_node; - } - }); - if (module_node) { - visitEachDescendant(module_node, [&](AST::AstNode *current_scope_node) { - if (current_scope_node->type == AST::AST_TYPEDEF || current_scope_node->type == AST::AST_PARAMETER || - current_scope_node->type == AST::AST_LOCALPARAM) { - AST_INTERNAL::current_scope[current_scope_node->str] = current_scope_node; - } - }); - } - // first apply custom simplification step if needed - simplify_sv(parameter, module_node); - // workaround for yosys sometimes not simplifying parameters children - // parameters can have 2 children: - // first child should be parameter value - // second child should be parameter range (optional) - log_assert(!parameter->children.empty()); - simplify_sv(parameter->children[0], parameter); - while (simplify(parameter->children[0], true, false, false, 1, -1, false, false)) { - } - // follow id2ast as yosys doesn't do it by default - if (parameter->children[0]->id2ast) { - simplify_sv(parameter->children[0]->id2ast, parameter); - while (simplify(parameter->children[0]->id2ast, true, false, false, 1, -1, false, false)) { - } - } - if (parameter->children.size() > 1) { - simplify_sv(parameter->children[1], parameter); - while (simplify(parameter->children[1], true, false, false, 1, -1, false, false)) { - } - if (parameter->children[1]->id2ast) { - simplify_sv(parameter->children[1]->id2ast, parameter); - while (simplify(parameter->children[1]->id2ast, true, false, false, 1, -1, false, false)) { - } - } - } - // then simplify parameter to AST_CONSTANT or AST_REALVALUE - while (simplify(parameter, true, false, false, 1, -1, false, false)) { - } - clear_current_scope(); -} - -void UhdmAst::process_module() -{ - std::string type = vpi_get_str(vpiDefName, obj_h); - std::string name = vpi_get_str(vpiName, obj_h) ? vpi_get_str(vpiName, obj_h) : type; - bool is_module_instance = type != name; - sanitize_symbol_name(type); - sanitize_symbol_name(name); - type = strip_package_name(type); - name = strip_package_name(name); - if (!is_module_instance) { - if (shared.top_nodes.find(type) != shared.top_nodes.end()) { - current_node = shared.top_nodes[type]; - shared.current_top_node = current_node; - auto process_it = std::find_if(current_node->children.begin(), current_node->children.end(), - [](auto node) { return node->type == AST::AST_INITIAL || node->type == AST::AST_ALWAYS; }); - auto children_after_process = std::vector(process_it, current_node->children.end()); - current_node->children.erase(process_it, current_node->children.end()); - auto old_top = shared.current_top_node; - shared.current_top_node = current_node; - visit_one_to_many({vpiModule, vpiInterface, vpiParameter, vpiParamAssign, vpiPort, vpiNet, vpiArrayNet, vpiTaskFunc, vpiGenScopeArray, - vpiContAssign, vpiVariables}, - obj_h, [&](AST::AstNode *node) { - if (node) { - if (get_attribute(node, attr_id::is_type_parameter)) { - // Don't process type parameters. - delete node; - return; - } - add_or_replace_child(current_node, node); - } - }); - // Primitives will have the same names (like "and"), so we need to make sure we don't replace them - visit_one_to_many({vpiPrimitive}, obj_h, [&](AST::AstNode *node) { - if (node) { - current_node->children.push_back(node); - } - }); - shared.current_top_node = old_top; - current_node->children.insert(current_node->children.end(), children_after_process.begin(), children_after_process.end()); - - delete_attribute(current_node, UhdmAst::partial()); - } else { - // processing nodes belonging to 'uhdmallModules' - current_node = make_ast_node(AST::AST_MODULE); - current_node->str = type; - shared.top_nodes[current_node->str] = current_node; - shared.current_top_node = current_node; - current_node->attributes[UhdmAst::partial()] = AST::AstNode::mkconst_int(1, false, 1); - visit_one_to_many({vpiTypedef}, obj_h, [&](AST::AstNode *node) { - if (node) { - move_type_to_new_typedef(current_node, node); - } - }); - visit_one_to_many({vpiModule, vpiParameter, vpiParamAssign, vpiNet, vpiArrayNet, vpiProcess}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (get_attribute(node, attr_id::is_type_parameter)) { - // Don't process type parameters. - delete node; - return; - } - if ((node->type == AST::AST_ASSIGN && node->children.size() < 2)) { - delete node; - return; - } - add_or_replace_child(current_node, node); - } - }); - } - } else { - // A module instance inside another uhdmTopModules' module. - // Create standalone module instance AST and embed it in the instantiating module using AST_CELL. - - const uhdm_handle *const handle = (const uhdm_handle *)obj_h; - const auto *const uhdm_obj = (const UHDM::any *)handle->object; - const auto current_instance_changer = ScopedValueChanger(shared.current_instance, uhdm_obj); - - current_node = make_ast_node(AST::AST_CELL); - std::vector> parameters; - - auto parameter_typedefs = make_unique_resource>(); - - visit_one_to_many({vpiParameter}, obj_h, [&](AST::AstNode *node) { - log_assert(node); - AST::AstNode *attr = get_attribute(node, attr_id::is_type_parameter); - - if (!attr) { - // Process type parameters only. - delete node; - return; - } - - if (node->children.size() == 0) { - log_assert(!attr->str.empty()); - // Anonymous types have no chidren, and store the parameter name in attr->str. - parameters.push_back(std::make_pair(node->str, attr->str)); - delete node; - return; - } - - for (auto child : node->children) { - if (child->type == AST::AST_TYPEDEF && !child->str.empty()) { - // process_type_parameter should have created a node with the parameter name - // and a child with the name of the value assigned to the parameter. - parameters.push_back(std::make_pair(node->str, child->str)); - } - - if (child->type == AST::AST_TYPEDEF || child->type == AST::AST_ENUM) { - // Copy definition of the type provided as parameter. - parameter_typedefs->push_back(child->clone()); - } - } - delete node; - }); - - visit_one_to_many({vpiParamAssign}, obj_h, [&](AST::AstNode *node) { - if (node && node->type == AST::AST_PARAMETER) { - log_assert(!node->children.empty()); - if (node->children[0]->type != AST::AST_CONSTANT) { - if (shared.top_nodes.count(type)) { - simplify_parameter(node, shared.top_nodes[type]); - } else { - simplify_parameter(node, nullptr); - } - } - log_assert(node->children[0]->type == AST::AST_CONSTANT || node->children[0]->type == AST::AST_REALVALUE); - parameters.push_back(std::make_pair(node->str, node->children[0]->asParaConst())); - } - delete node; - }); - // We need to rename module to prevent name collision with the same module, but with different parameters - std::string module_name = !parameters.empty() ? AST::derived_module_name(type, parameters).c_str() : type; - auto module_node = shared.top_nodes[module_name]; - // true, when Surelog don't have definition of module while parsing design - // if so, we leaving module parameters to yosys and don't rename module - // as it will be done by yosys - bool isPrimitive = false; - if (!module_node) { - module_node = shared.top_nodes[type]; - if (!module_node) { - module_node = new AST::AstNode(AST::AST_MODULE); - module_node->str = type; - module_node->attributes[UhdmAst::partial()] = AST::AstNode::mkconst_int(2, false, 1); - module_node->attributes[ID::whitebox] = AST::AstNode::mkconst_int(1, false, 1); - } - isPrimitive = module_node->attributes.count(UhdmAst::partial()) && module_node->attributes[UhdmAst::partial()]->integer == 2; - if (!parameters.empty() && !isPrimitive) { - module_node = module_node->clone(); - module_node->str = module_name; - } - } else if (auto attribute = get_attribute(module_node, attr_id::is_elaborated_module); attribute && attribute->integer == 1) { - // we already processed module with this parameters, just create cell node - make_cell(obj_h, current_node, module_node); - return; - } - shared.top_nodes[module_node->str] = module_node; - visit_one_to_many({vpiParamAssign}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (node->children[0]->type != AST::AST_CONSTANT) { - if (shared.top_nodes[type]) { - simplify_parameter(node, module_node); - log_assert(node->children[0]->type == AST::AST_CONSTANT || node->children[0]->type == AST::AST_REALVALUE); - } - } - // if module is primitive - // Surelog doesn't have definition of this module, - // so we need to left setting of parameters to yosys - if (isPrimitive) { - node->type = AST::AST_PARASET; - current_node->children.push_back(node); - } else { - add_or_replace_child(module_node, node); - } - } - }); - module_node->children.insert(std::end(module_node->children), std::begin(*parameter_typedefs), std::end(*parameter_typedefs)); - parameter_typedefs->clear(); - parameter_typedefs.reset(); - if (module_node->attributes.count(UhdmAst::partial())) { - AST::AstNode *attr = module_node->attributes.at(UhdmAst::partial()); - if (attr->type == AST::AST_CONSTANT) - if (attr->integer == 1) { - delete_attribute(module_node, UhdmAst::partial()); - } - } - auto typeNode = new AST::AstNode(AST::AST_CELLTYPE); - typeNode->str = module_node->str; - current_node->children.insert(current_node->children.begin(), typeNode); - auto old_top = shared.current_top_node; - shared.current_top_node = module_node; - visit_one_to_many({vpiVariables, vpiNet, vpiArrayNet, vpiInterface, vpiModule, vpiPort, vpiGenScopeArray, vpiContAssign, vpiTaskFunc}, obj_h, - [&](AST::AstNode *node) { - if (node) { - add_or_replace_child(module_node, node); - } - }); - make_cell(obj_h, current_node, module_node); - shared.current_top_node = old_top; - set_attribute(module_node, attr_id::is_elaborated_module, AST::AstNode::mkconst_int(1, true)); - } -} - -void UhdmAst::process_struct_typespec() -{ - current_node = make_ast_node(AST::AST_STRUCT); - visit_one_to_many({vpiTypespecMember}, obj_h, [&](AST::AstNode *node) { - if (node->children.size() > 0 && node->children[0]->type == AST::AST_ENUM) { - log_assert(node->children.size() == 1); - log_assert(!node->children[0]->children.empty()); - log_assert(!node->children[0]->children[0]->children.empty()); - // TODO: add missing enum_type attribute - AST::AstNode *range = nullptr; - // check if single enum element is larger than 1 bit - if (node->children[0]->children[0]->children.size() == 2) { - range = node->children[0]->children[0]->children[1]->clone(); - } else { - range = make_range(0, 0); - } - delete_children(node); - node->children.push_back(range); - } - current_node->children.push_back(node); - }); -} - -void UhdmAst::process_union_typespec() -{ - current_node = make_ast_node(AST::AST_UNION); - visit_one_to_many({vpiTypespecMember}, obj_h, [&](AST::AstNode *node) { - if (node->children.size() > 0 && node->children[0]->type == AST::AST_ENUM) { - log_assert(node->children.size() == 1); - log_assert(!node->children[0]->children.empty()); - log_assert(!node->children[0]->children[0]->children.empty()); - // TODO: add missing enum_type attribute - AST::AstNode *range = nullptr; - // check if single enum element is larger than 1 bit - if (node->children[0]->children[0]->children.size() == 2) { - range = node->children[0]->children[0]->children[1]->clone(); - } else { - range = make_range(0, 0); - } - delete_children(node); - node->children.push_back(range); - } - current_node->children.push_back(node); - }); -} - -void UhdmAst::process_array_typespec() -{ - current_node = make_ast_node(AST::AST_WIRE); - std::vector packed_ranges; - std::vector unpacked_ranges; - visit_one_to_one({vpiElemTypespec}, obj_h, [&](AST::AstNode *node) { - if (node && node->type == AST::AST_STRUCT) { - auto str = current_node->str; - node->cloneInto(current_node); - current_node->str = str; - delete node; - } - }); - if (auto elemtypespec_h = vpi_handle(vpiElemTypespec, obj_h)) { - visit_one_to_many({vpiRange}, elemtypespec_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - vpi_release_handle(elemtypespec_h); - } - visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { unpacked_ranges.push_back(node); }); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); -} - -void UhdmAst::process_typespec_member() -{ - std::vector packed_ranges; - std::vector unpacked_ranges; - current_node = make_ast_node(AST::AST_STRUCT_ITEM); - current_node->str = current_node->str.substr(1); - vpiHandle typespec_h = vpi_handle(vpiTypespec, obj_h); - int typespec_type = vpi_get(vpiType, typespec_h); - const uhdm_handle *const handle = (const uhdm_handle *)typespec_h; - const UHDM::BaseClass *const object = (const UHDM::BaseClass *)handle->object; - switch (typespec_type) { - case vpiBitTypespec: - case vpiLogicTypespec: { - current_node->is_logic = true; - visit_one_to_many({vpiRange}, typespec_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - break; - } - case vpiByteTypespec: { - current_node->is_signed = vpi_get(vpiSigned, typespec_h); - packed_ranges.push_back(make_range(7, 0)); - break; - } - case vpiShortIntTypespec: { - current_node->is_signed = vpi_get(vpiSigned, typespec_h); - packed_ranges.push_back(make_range(15, 0)); - break; - } - case vpiIntTypespec: - case vpiIntegerTypespec: { - current_node->is_signed = vpi_get(vpiSigned, typespec_h); - packed_ranges.push_back(make_range(31, 0)); - break; - } - case vpiTimeTypespec: - case vpiLongIntTypespec: { - current_node->is_signed = vpi_get(vpiSigned, typespec_h); - packed_ranges.push_back(make_range(63, 0)); - break; - } - case vpiStructTypespec: - case vpiUnionTypespec: - case vpiEnumTypespec: { - visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) { - if (typespec_type == vpiStructTypespec || typespec_type == vpiUnionTypespec) { - auto str = current_node->str; - node->cloneInto(current_node); - current_node->str = str; - delete node; - } else if (typespec_type == vpiEnumTypespec) { - current_node->children.push_back(node); - } else { - delete node; - } - }); - break; - } - case vpiPackedArrayTypespec: - visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) { - if (node && node->type == AST::AST_STRUCT) { - auto str = current_node->str; - if (node->attributes.count(UhdmAst::packed_ranges())) { - for (auto r : node->attributes[UhdmAst::packed_ranges()]->children) { - packed_ranges.push_back(r->clone()); - } - std::reverse(packed_ranges.begin(), packed_ranges.end()); - delete_attribute(node, UhdmAst::packed_ranges()); - } - if (node->attributes.count(UhdmAst::unpacked_ranges())) { - for (auto r : node->attributes[UhdmAst::unpacked_ranges()]->children) { - unpacked_ranges.push_back(r->clone()); - } - delete_attribute(node, UhdmAst::unpacked_ranges()); - } - node->cloneInto(current_node); - current_node->str = str; - current_node->children.insert(current_node->children.end(), packed_ranges.begin(), packed_ranges.end()); - packed_ranges.clear(); - delete node; - } else if (node) { - auto str = current_node->str; - if (node->attributes.count(UhdmAst::packed_ranges())) { - for (auto r : node->attributes[UhdmAst::packed_ranges()]->children) { - packed_ranges.push_back(r->clone()); - } - std::reverse(packed_ranges.begin(), packed_ranges.end()); - delete_attribute(node, UhdmAst::packed_ranges()); - } - if (node->attributes.count(UhdmAst::unpacked_ranges())) { - for (auto r : node->attributes[UhdmAst::unpacked_ranges()]->children) { - unpacked_ranges.push_back(r->clone()); - } - delete_attribute(node, UhdmAst::unpacked_ranges()); - } - node->cloneInto(current_node); - current_node->str = str; - current_node->type = AST::AST_STRUCT_ITEM; - delete node; - } - }); - break; - case vpiVoidTypespec: { - report_error("%.*s:%d: Void typespecs are currently unsupported", (int)object->VpiFile().length(), object->VpiFile().data(), - object->VpiLineNo()); - break; - } - case vpiClassTypespec: { - report_error("%.*s:%d: Class typespecs are unsupported", (int)object->VpiFile().length(), object->VpiFile().data(), object->VpiLineNo()); - break; - } - default: { - report_error("%.*s:%d: Encountered unhandled typespec in process_typespec_member: '%.*s' of type '%s'\n", (int)object->VpiFile().length(), - object->VpiFile().data(), object->VpiLineNo(), (int)object->VpiName().length(), object->VpiName().data(), - UHDM::VpiTypeName(typespec_h).c_str()); - break; - } - } - vpi_release_handle(typespec_h); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); -} - -static UHDM::expr *reduce_expression(const UHDM::any *expr, const UHDM::any *inst, const UHDM::any *pexpr) -{ - log_assert(expr); - log_assert(inst); - - bool invalidvalue = false; - UHDM::ExprEval eval; - UHDM::expr *resolved_operation = eval.reduceExpr(expr, invalidvalue, inst, pexpr); - if (invalidvalue) { - log_file_warning(std::string(expr->VpiFile()), expr->VpiLineNo(), "Could not reduce expression.\n"); - } - return resolved_operation; -} - -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); - - const uhdm_handle *const handle = (const uhdm_handle *)obj_h; - const auto *enum_object = (const UHDM::enum_typespec *)handle->object; - const auto *typespec = enum_object->Base_typespec(); - - if (current_node->str.empty()) { - // anonymous typespec, check if not already created - log_assert(shared.current_top_node); - auto check_created_anonymous_enums = [enum_object, this](std::string top_module_name) -> bool { - for (auto pair : shared.anonymous_enums[top_module_name]) { - UHDM::CompareContext ctx; - if (pair.first->Compare(enum_object, &ctx) == 0) { - // we already created typedef for this. - delete current_node; - current_node = make_node(AST::AST_WIRETYPE); - current_node->str = pair.second; - return true; - } - } - return false; - }; - std::string top_module_name = shared.current_top_node->str; - if (check_created_anonymous_enums(top_module_name)) { - return; - } - // in case of parametrized module, also check unparametrized top module - // as we could add this enum there and then copy it to parametrized - // version - if (top_module_name.find("$paramod") != std::string::npos) { - // possible names: - // $paramod\module_name\PARAM=VAL - // $paramod$81af6bf473845aee480c993b90a1ed0117ae9091\module_name - top_module_name = top_module_name.substr(top_module_name.find("\\")); - if (auto params = top_module_name.find("\\", 1 /* skip first \ */) != std::string::npos) - top_module_name = top_module_name.substr(0, params); - } - if (check_created_anonymous_enums(top_module_name)) { - return; - } - } - - if (typespec && typespec->UhdmType() == UHDM::uhdmlogic_typespec) { - // If it's a logic_typespec, try to reduce expressions inside of it. - // The `reduceExpr` function needs the whole context of the enum typespec - // so it's called here instead of `process_operation` or any other more specific function. - - const UHDM::logic_typespec *logic_typespec_obj = enum_object->Base_typespec()->Cast(); - std::vector ranges; - // Check if ranges exist, as Ranges() returns a pointer to std::vector. - if (logic_typespec_obj->Ranges()) { - ranges = *(logic_typespec_obj->Ranges()); - } - for (UHDM::range *range_obj : ranges) { - // For each range, take both left and right and reduce them if they're of type uhdmoperation. - const auto *leftrange_obj = range_obj->Left_expr(); - const auto *rightrange_obj = range_obj->Right_expr(); - log_assert(leftrange_obj); - log_assert(rightrange_obj); - - if (leftrange_obj->UhdmType() == UHDM::uhdmoperation) { - // Substitute the previous leftrange with the resolved operation result. - const UHDM::any *const instance = - enum_object->Instance() ? enum_object->Instance() : enum_object->VpiParent() ? enum_object->VpiParent() : shared.current_instance; - - range_obj->Left_expr(reduce_expression(leftrange_obj, instance, enum_object->VpiParent())); - } - if (rightrange_obj->UhdmType() == UHDM::uhdmoperation) { - // Substitute the previous rightrange with the resolved operation result. - const UHDM::any *const instance = - enum_object->Instance() ? enum_object->Instance() : enum_object->VpiParent() ? enum_object->VpiParent() : shared.current_instance; - - range_obj->Right_expr(reduce_expression(rightrange_obj, instance, enum_object->VpiParent())); - } - } - } - - 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 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; - } - }); - 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; - } - // IMPORTANT: invalidates `range_it`! - current_node->children.push_back(node); - }); - if (range) { - delete range; - } - if (current_node->str.empty()) { - // anonymous typespec - std::string typedef_name = "$systemverilog_plugin$anonymous_enum" + std::to_string(shared.next_anonymous_enum_typedef_id()); - current_node->str = typedef_name; - uhdmast_assert(shared.current_top_node != nullptr); - move_type_to_new_typedef(shared.current_top_node, current_node); - current_node = make_node(AST::AST_WIRETYPE); - current_node->str = typedef_name; - shared.anonymous_enums[shared.current_top_node->str][enum_object] = std::move(typedef_name); - } -} - -void UhdmAst::process_enum_const() -{ - current_node = make_ast_node(AST::AST_ENUM_ITEM); - AST::AstNode *constant_node = process_value(obj_h); - if (constant_node) { - constant_node->filename = current_node->filename; - constant_node->location = current_node->location; - current_node->children.push_back(constant_node); - } -} - -void UhdmAst::process_custom_var() -{ - current_node = make_ast_node(AST::AST_WIRE); - visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) { - if (node->str.empty()) { - // anonymous typespec, move the children to variable - current_node->type = node->type; - copy_packed_unpacked_attribute(node, current_node); - current_node->children = std::move(node->children); - } else { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - } - delete node; - }); - auto type = vpi_get(vpiType, obj_h); - if (type == vpiEnumVar || type == vpiStructVar || type == vpiUnionVar) { - visit_default_expr(obj_h); - } - current_node->is_custom_type = true; -} - -void UhdmAst::process_int_var() -{ - current_node = make_ast_node(AST::AST_WIRE); - auto left_const = AST::AstNode::mkconst_int(31, true); - auto right_const = AST::AstNode::mkconst_int(0, true); - auto range = new AST::AstNode(AST::AST_RANGE, left_const, right_const); - current_node->children.push_back(range); - current_node->is_signed = vpi_get(vpiSigned, obj_h); - visit_default_expr(obj_h); -} - -void UhdmAst::process_real_var() -{ - auto module_node = find_ancestor({AST::AST_MODULE}); - auto wire_node = make_ast_node(AST::AST_WIRE); - auto left_const = AST::AstNode::mkconst_int(63, true); - auto right_const = AST::AstNode::mkconst_int(0, true); - auto range = new AST::AstNode(AST::AST_RANGE, left_const, right_const); - wire_node->children.push_back(range); - wire_node->is_signed = true; - module_node->children.push_back(wire_node); - current_node = make_ast_node(AST::AST_IDENTIFIER); - visit_default_expr(obj_h); -} - -void UhdmAst::process_array_var() -{ - current_node = make_ast_node(AST::AST_WIRE); - std::vector packed_ranges; - std::vector unpacked_ranges; - visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) { - if (node->str.empty()) { - // anonymous typespec, move the children to variable - current_node->type = node->type; - current_node->children = std::move(node->children); - } else { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - current_node->is_custom_type = true; - } - delete node; - }); - vpiHandle itr = vpi_iterate(vpi_get(vpiType, obj_h) == vpiArrayVar ? vpiReg : vpiElement, obj_h); - while (vpiHandle reg_h = vpi_scan(itr)) { - if (vpi_get(vpiType, reg_h) == vpiStructVar || vpi_get(vpiType, reg_h) == vpiEnumVar) { - visit_one_to_one({vpiTypespec}, reg_h, [&](AST::AstNode *node) { - if (node->str.empty()) { - // anonymous typespec, move the children to variable - current_node->type = node->type; - current_node->children = std::move(node->children); - } else { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - current_node->is_custom_type = true; - } - delete node; - }); - } else if (vpi_get(vpiType, reg_h) == vpiLogicVar) { - current_node->is_logic = true; - visit_one_to_one({vpiTypespec}, reg_h, [&](AST::AstNode *node) { - if (node->str.empty()) { - // anonymous typespec, move the children to variable - current_node->type = node->type; - current_node->children = std::move(node->children); - } else { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - current_node->is_custom_type = true; - } - delete node; - }); - visit_one_to_many({vpiRange}, reg_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - } else if (vpi_get(vpiType, reg_h) == vpiIntVar) { - packed_ranges.push_back(make_range(31, 0)); - visit_default_expr(reg_h); - } - vpi_release_handle(reg_h); - } - vpi_release_handle(itr); - visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { unpacked_ranges.push_back(node); }); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); - visit_default_expr(obj_h); -} - -void UhdmAst::process_packed_array_var() -{ - current_node = make_ast_node(AST::AST_WIRE); - std::vector packed_ranges; - std::vector unpacked_ranges; - visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) { - if (node->str.empty()) { - // anonymous typespec, move the children to variable - current_node->type = node->type; - current_node->children = std::move(node->children); - } else { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - current_node->is_custom_type = true; - } - delete node; - }); - vpiHandle itr = vpi_iterate(vpi_get(vpiType, obj_h) == vpiArrayVar ? vpiReg : vpiElement, obj_h); - while (vpiHandle reg_h = vpi_scan(itr)) { - if (vpi_get(vpiType, reg_h) == vpiStructVar || vpi_get(vpiType, reg_h) == vpiEnumVar) { - visit_one_to_one({vpiTypespec}, reg_h, [&](AST::AstNode *node) { - if (node->str.empty()) { - // anonymous typespec, move the children to variable - current_node->type = node->type; - current_node->children = std::move(node->children); - } else { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - current_node->is_custom_type = true; - } - delete node; - }); - } else if (vpi_get(vpiType, reg_h) == vpiLogicVar) { - current_node->is_logic = true; - visit_one_to_one({vpiTypespec}, reg_h, [&](AST::AstNode *node) { - if (node->str.empty()) { - // anonymous typespec, move the children to variable - current_node->type = node->type; - current_node->children = std::move(node->children); - } else { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - current_node->is_custom_type = true; - } - delete node; - }); - visit_one_to_many({vpiRange}, reg_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - } else if (vpi_get(vpiType, reg_h) == vpiIntVar) { - packed_ranges.push_back(make_range(31, 0)); - visit_default_expr(reg_h); - } - vpi_release_handle(reg_h); - } - vpi_release_handle(itr); - visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); - visit_default_expr(obj_h); -} - -void UhdmAst::process_param_assign() -{ - current_node = make_ast_node(AST::AST_PARAMETER); - visit_one_to_one({vpiLhs}, obj_h, [&](AST::AstNode *node) { - if (node) { - current_node->type = node->type; - current_node->str = node->str; - // Here we need to copy any ranges that is already present in lhs, - // but we want to skip actual value, as it is set in rhs - for (auto *c : node->children) { - if (c->type != AST::AST_CONSTANT) { - current_node->children.push_back(c->clone()); - } - } - delete_children(node); - copy_packed_unpacked_attribute(node, current_node); - current_node->is_custom_type = node->is_custom_type; - auto it = shared.param_types.find(current_node->str); - if (it == shared.param_types.end()) - shared.param_types[current_node->str] = shared.param_types[node->str]; - delete node; - } - }); - visit_one_to_one({vpiRhs}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (node->children.size() > 1 && (node->children[1]->type == AST::AST_PARAMETER || node->children[1]->type == AST::AST_LOCALPARAM)) { - node->children[1]->type = AST::AST_IDENTIFIER; - } - current_node->children.insert(current_node->children.begin(), node); - } - }); -} - -void UhdmAst::process_cont_assign_var_init() -{ - current_node = make_ast_node(AST::AST_INITIAL); - auto block_node = make_ast_node(AST::AST_BLOCK); - auto assign_node = make_ast_node(AST::AST_ASSIGN_LE); - block_node->children.push_back(assign_node); - current_node->children.push_back(block_node); - - visit_one_to_one({vpiLhs, vpiRhs}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (node->type == AST::AST_WIRE || node->type == AST::AST_PARAMETER || node->type == AST::AST_LOCALPARAM) { - assign_node->children.push_back(new AST::AstNode(AST::AST_IDENTIFIER)); - assign_node->children.back()->str = node->str; - delete node; - } else { - assign_node->children.push_back(node); - } - } - }); -} - -void UhdmAst::process_cont_assign_net() -{ - current_node = make_ast_node(AST::AST_ASSIGN); - - visit_one_to_one({vpiLhs, vpiRhs}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (node->type == AST::AST_WIRE || node->type == AST::AST_PARAMETER || node->type == AST::AST_LOCALPARAM) { - current_node->children.push_back(new AST::AstNode(AST::AST_IDENTIFIER)); - current_node->children.back()->str = node->str; - } else { - current_node->children.push_back(node->clone()); - } - delete node; - } - }); -} - -void UhdmAst::process_cont_assign() -{ - auto net_decl_assign = vpi_get(vpiNetDeclAssign, obj_h); - vpiHandle node_lhs_h = vpi_handle(vpiLhs, obj_h); - auto lhs_net_type = vpi_get(vpiNetType, node_lhs_h); - vpi_release_handle(node_lhs_h); - - // Check if lhs is a subtype of a net - bool isNet; - if (lhs_net_type >= vpiWire && lhs_net_type <= vpiUwire) - isNet = true; - else - // lhs is a variable - isNet = false; - if (net_decl_assign && !isNet) - process_cont_assign_var_init(); - else - process_cont_assign_net(); -} - -void UhdmAst::process_assignment(const UHDM::BaseClass *object) -{ - auto type = vpi_get(vpiBlocking, obj_h) == 1 ? AST::AST_ASSIGN_EQ : AST::AST_ASSIGN_LE; - bool shift_unsigned = false; - int op_type = vpi_get(vpiOpType, obj_h); - AST::AstNodeType node_type; - current_node = make_ast_node(type); - - visit_one_to_one({vpiLhs, vpiRhs}, obj_h, [&](AST::AstNode *node) { - if (node) { - // fix node types for some assignments - // yosys requires that declaration of variable - // and assignment are separated - switch (node->type) { - case AST::AST_WIRE: - // wires can be declarated inside initialization block of for block - if (AST::AstNode *for_block = find_ancestor({AST::AST_BLOCK})) { - if (for_block->str.find("$fordecl_block") != std::string::npos) - break; - } - [[fallthrough]]; - case AST::AST_PARAMETER: - case AST::AST_LOCALPARAM: - node->type = AST::AST_IDENTIFIER; - delete_children(node); - delete_attribute(node, UhdmAst::packed_ranges()); - delete_attribute(node, UhdmAst::unpacked_ranges()); - break; - default: - break; - }; - current_node->children.push_back(node); - } - }); - if (op_type && op_type != vpiAssignmentOp) { - switch (op_type) { - case vpiSubOp: - node_type = AST::AST_SUB; - break; - case vpiDivOp: - node_type = AST::AST_DIV; - break; - case vpiModOp: - node_type = AST::AST_MOD; - break; - case vpiLShiftOp: - node_type = AST::AST_SHIFT_LEFT; - shift_unsigned = true; - break; - case vpiRShiftOp: - node_type = AST::AST_SHIFT_RIGHT; - shift_unsigned = true; - break; - case vpiAddOp: - node_type = AST::AST_ADD; - break; - case vpiMultOp: - node_type = AST::AST_MUL; - break; - case vpiBitAndOp: - node_type = AST::AST_BIT_AND; - break; - case vpiBitOrOp: - node_type = AST::AST_BIT_OR; - break; - case vpiBitXorOp: - node_type = AST::AST_BIT_XOR; - break; - case vpiArithLShiftOp: - node_type = AST::AST_SHIFT_SLEFT; - shift_unsigned = true; - break; - case vpiArithRShiftOp: - node_type = AST::AST_SHIFT_SRIGHT; - shift_unsigned = true; - break; - default: - delete current_node; - current_node = nullptr; - report_error("%.*s:%d: Encountered unhandled compound assignment with operation type %d\n", (int)object->VpiFile().length(), - object->VpiFile().data(), object->VpiLineNo(), op_type); - return; - } - log_assert(current_node->children.size() == 2); - auto child_node = new AST::AstNode(node_type, current_node->children[0]->clone(), current_node->children[1]); - current_node->children[1] = child_node; - if (shift_unsigned) { - log_assert(current_node->children[1]->children.size() == 2); - auto unsigned_node = new AST::AstNode(AST::AST_TO_UNSIGNED, current_node->children[1]->children[1]); - current_node->children[1]->children[1] = unsigned_node; - } - } - if (current_node->children.size() == 1 && current_node->children[0]->type == AST::AST_WIRE) { - auto top_node = find_ancestor({AST::AST_MODULE}); - if (!top_node) - return; - top_node->children.push_back(std::move(current_node->children[0])); - delete current_node; - current_node = nullptr; - } -} - -void UhdmAst::process_packed_array_net() -{ - std::vector packed_ranges; - std::vector unpacked_ranges; - current_node = make_ast_node(AST::AST_WIRE); - visit_one_to_many({vpiElement}, obj_h, [&](AST::AstNode *node) { - if (node && GetSize(node->children) == 1) - current_node->children.push_back(node->children[0]->clone()); - current_node->is_custom_type = node->is_custom_type; - delete node; - }); - visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); -} - -void UhdmAst::process_array_net(const UHDM::BaseClass *object) -{ - current_node = make_ast_node(AST::AST_WIRE); - vpiHandle itr = vpi_iterate(vpiNet, obj_h); - std::vector packed_ranges; - std::vector unpacked_ranges; - while (vpiHandle net_h = vpi_scan(itr)) { - auto net_type = vpi_get(vpiType, net_h); - if (net_type == vpiLogicNet) { - current_node->is_logic = true; - current_node->is_signed = vpi_get(vpiSigned, net_h); - vpiHandle typespec_h = vpi_handle(vpiTypespec, net_h); - if (!typespec_h) { - typespec_h = vpi_handle(vpiTypespec, obj_h); - } - if (typespec_h) { - visit_one_to_many({vpiRange}, typespec_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - vpi_release_handle(typespec_h); - } else { - visit_one_to_many({vpiRange}, net_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - } - } else if (net_type == vpiStructNet) { - visit_one_to_one({vpiTypespec}, net_h, [&](AST::AstNode *node) { - if (node->str.empty()) { - // anonymous typespec, move the children to variable - current_node->type = node->type; - current_node->children = std::move(node->children); - } else { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - current_node->is_custom_type = true; - } - delete node; - }); - } - vpi_release_handle(net_h); - } - vpi_release_handle(itr); - visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { unpacked_ranges.push_back(node); }); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); -} - -void UhdmAst::process_package() -{ - current_node = make_ast_node(AST::AST_PACKAGE); - shared.current_top_node = current_node; - visit_one_to_many({vpiTypedef}, obj_h, [&](AST::AstNode *node) { - if (node) { - move_type_to_new_typedef(current_node, node); - } - }); - visit_one_to_many({vpiParameter, vpiParamAssign}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (get_attribute(node, attr_id::is_type_parameter)) { - // Don't process type parameters. - delete node; - return; - } - node->str = strip_package_name(node->str); - for (auto c : node->children) { - c->str = strip_package_name(c->str); - } - add_or_replace_child(current_node, node); - } - }); - visit_one_to_many({vpiTaskFunc}, obj_h, [&](AST::AstNode *node) { - if (node) { - current_node->children.push_back(node); - } - }); -} - -void UhdmAst::process_interface() -{ - std::string type = vpi_get_str(vpiDefName, obj_h); - std::string name = vpi_get_str(vpiName, obj_h) ? vpi_get_str(vpiName, obj_h) : type; - sanitize_symbol_name(type); - sanitize_symbol_name(name); - AST::AstNode *elaboratedInterface; - // Check if we have encountered this object before - if (shared.top_nodes.find(type) != shared.top_nodes.end()) { - // Was created before, fill missing - elaboratedInterface = shared.top_nodes[type]; - visit_one_to_many({vpiPort, vpiVariables}, obj_h, [&](AST::AstNode *node) { - if (node) { - add_or_replace_child(elaboratedInterface, node); - } - }); - } else { - // Encountered for the first time - elaboratedInterface = new AST::AstNode(AST::AST_INTERFACE); - elaboratedInterface->str = name; - visit_one_to_many({vpiNet, vpiPort, vpiModport}, obj_h, [&](AST::AstNode *node) { - if (node) { - add_or_replace_child(elaboratedInterface, node); - } - }); - } - shared.top_nodes[elaboratedInterface->str] = elaboratedInterface; - if (name != type) { - // Not a top module, create instance - current_node = make_ast_node(AST::AST_CELL); - make_cell(obj_h, current_node, elaboratedInterface); - } else { - current_node = elaboratedInterface; - } -} - -void UhdmAst::process_modport() -{ - current_node = make_ast_node(AST::AST_MODPORT); - visit_one_to_many({vpiIODecl}, obj_h, [&](AST::AstNode *node) { - if (node) { - current_node->children.push_back(node); - } - }); -} - -void UhdmAst::process_io_decl() -{ - current_node = nullptr; - std::vector packed_ranges; // comes before wire name - std::vector unpacked_ranges; // comes after wire name - visit_one_to_one({vpiExpr}, obj_h, [&](AST::AstNode *node) { current_node = node; }); - if (current_node == nullptr) { - current_node = make_ast_node(AST::AST_MODPORTMEMBER); - visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { unpacked_ranges.push_back(node); }); - } - std::reverse(unpacked_ranges.begin(), unpacked_ranges.end()); - - visit_one_to_one({vpiTypedef}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (!node->str.empty()) { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - // wiretype needs to be 1st node (if port have also another range nodes) - current_node->children.insert(current_node->children.begin(), wiretype_node); - current_node->is_custom_type = true; - } else { - // anonymous typedef, just move children - for (auto child : node->children) { - current_node->children.push_back(child->clone()); - } - if (node->attributes.count(UhdmAst::packed_ranges())) { - for (auto r : node->attributes[UhdmAst::packed_ranges()]->children) { - packed_ranges.push_back(r->clone()); - } - } - if (node->attributes.count(UhdmAst::unpacked_ranges())) { - for (auto r : node->attributes[UhdmAst::unpacked_ranges()]->children) { - unpacked_ranges.push_back(r->clone()); - } - } - current_node->is_logic = node->is_logic; - current_node->is_reg = node->is_reg; - } - current_node->is_signed = node->is_signed; - delete node; - } - }); - if (const int n = vpi_get(vpiDirection, obj_h)) { - if (n == vpiInput) { - current_node->is_input = true; - } else if (n == vpiOutput) { - current_node->is_output = true; - } else if (n == vpiInout) { - current_node->is_input = true; - current_node->is_output = true; - } - } - add_multirange_wire(current_node, packed_ranges, unpacked_ranges, false); -} - -void UhdmAst::process_always() -{ - current_node = make_ast_node(AST::AST_ALWAYS); - visit_one_to_one({vpiStmt}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (node->type != AST::AST_BLOCK) { - // Create implicit block. - AST::AstNode *block = make_ast_node(AST::AST_BLOCK); - // There are (at least) two cases where something could have been inserted into AST_ALWAYS node when `node` is not an AST_BLOCK: - // - stream_op inserts a block. - // - event_control inserts a non-block statement. - // Move the block inserted by a stream_op into an implicit group. Everything else stays where it is. - if (!current_node->children.empty() && current_node->children.back()->type == AST::AST_BLOCK) { - block->children.push_back(current_node->children.back()); - current_node->children.pop_back(); - } - block->children.push_back(node); - current_node->children.push_back(block); - } else { - // Child is an explicit block. - current_node->children.push_back(node); - } - } else { - // TODO (mglb): This branch is probably unreachable? Is it possible to have empty `always`? - // No children, so nothing should have been inserted into the always node during visitation. - log_assert(current_node->children.empty()); - // Create implicit empty block. - current_node->children.push_back(make_ast_node(AST::AST_BLOCK)); - } - }); - switch (vpi_get(vpiAlwaysType, obj_h)) { - case vpiAlwaysComb: - current_node->attributes[ID::always_comb] = AST::AstNode::mkconst_int(1, false); - break; - case vpiAlwaysFF: - current_node->attributes[ID::always_ff] = AST::AstNode::mkconst_int(1, false); - break; - case vpiAlwaysLatch: - current_node->attributes[ID::always_latch] = AST::AstNode::mkconst_int(1, false); - break; - default: - break; - } -} - -void UhdmAst::process_event_control(const UHDM::BaseClass *object) -{ - current_node = make_ast_node(AST::AST_BLOCK); - visit_one_to_one({vpiCondition}, obj_h, [&](AST::AstNode *node) { - if (node) { - auto process_node = find_ancestor({AST::AST_ALWAYS}); - if (!process_node) { - log_error("%.*s:%d: Currently supports only event control stmts inside 'always'\n", (int)object->VpiFile().length(), - object->VpiFile().data(), object->VpiLineNo()); - } - process_node->children.push_back(node); - } - // is added inside vpiOperation - }); - visit_one_to_one({vpiStmt}, obj_h, [&](AST::AstNode *node) { - if (node) { - current_node->children.push_back(node); - } - }); -} - -void UhdmAst::process_initial() -{ - current_node = make_ast_node(AST::AST_INITIAL); - // TODO (mglb): handler below is identical as in `process_always`. Extract it to avoid duplication. - visit_one_to_one({vpiStmt}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (node->type != AST::AST_BLOCK) { - // Create an implicit block. - AST::AstNode *block = make_ast_node(AST::AST_BLOCK); - // There is (at least) one case where something could have been inserted into AST_INITIAL node when `node` is not an AST_BLOCK: - // - stream_op inserts a block. - // Move the block inserted by a stream_op into an implicit group. - if (!current_node->children.empty() && current_node->children.back()->type == AST::AST_BLOCK) { - block->children.push_back(current_node->children.back()); - current_node->children.pop_back(); - } - block->children.push_back(node); - current_node->children.push_back(block); - } else { - // Child is an explicit block. - current_node->children.push_back(node); - } - } else { - // TODO (mglb): This branch is probably unreachable? Is it possible to have empty `initial`? - // No children, so nothing should have been inserted into the always node during visitation. - log_assert(current_node->children.empty()); - // Create implicit empty block. - current_node->children.push_back(make_ast_node(AST::AST_BLOCK)); - } - }); -} - -void UhdmAst::process_begin(bool is_named) -{ - current_node = make_ast_node(AST::AST_BLOCK); - if (!is_named) { - // for unnamed block, reset block name - current_node->str = ""; - } - AST::AstNode *hierarchy_node = nullptr; - static int unnamed_block_idx = 0; - visit_one_to_many({vpiVariables}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (!is_named) { - if (!hierarchy_node) { - // Create an implicit hierarchy scope - // simplify checks if sv_mode is set to true when wire is declared inside unnamed block - VERILOG_FRONTEND::sv_mode = true; - hierarchy_node = make_ast_node(AST::AST_BLOCK); - hierarchy_node->str = "$unnamed_block$" + std::to_string(unnamed_block_idx++); - } - hierarchy_node->children.push_back(node); - } else { - current_node->children.push_back(node); - } - } - }); - visit_one_to_many({vpiStmt}, obj_h, [&](AST::AstNode *node) { - if (node) { - if ((node->type == AST::AST_ASSIGN_EQ || node->type == AST::AST_ASSIGN_LE) && node->children.size() == 1) { - auto func_node = find_ancestor({AST::AST_FUNCTION, AST::AST_TASK}); - if (!func_node) { - delete node; - return; - } - auto wire_node = new AST::AstNode(AST::AST_WIRE); - wire_node->type = AST::AST_WIRE; - wire_node->str = node->children[0]->str; - func_node->children.push_back(wire_node); - delete node; - } else { - if (hierarchy_node) - hierarchy_node->children.push_back(node); - else - current_node->children.push_back(node); - } - } - }); - if (hierarchy_node) - current_node->children.push_back(hierarchy_node); -} - -void UhdmAst::process_operation(const UHDM::BaseClass *object) -{ - auto operation = vpi_get(vpiOpType, obj_h); - switch (operation) { - case vpiStreamRLOp: - process_stream_op(); - break; - case vpiEventOrOp: - case vpiListOp: - process_list_op(); - break; - case vpiCastOp: - process_cast_op(); - break; - case vpiInsideOp: - process_inside_op(); - break; - case vpiAssignmentPatternOp: - process_assignment_pattern_op(); - break; - case vpiWildEqOp: - case vpiWildNeqOp: { - report_error("%.*s:%d: Wildcard operators are not supported yet\n", (int)object->VpiFile().length(), object->VpiFile().data(), - object->VpiLineNo()); - break; - } - default: { - current_node = make_ast_node(AST::AST_NONE); - visit_one_to_many({vpiOperand}, obj_h, [&](AST::AstNode *node) { - if (node) { - current_node->children.push_back(node); - } - }); - switch (operation) { - case vpiMinusOp: - current_node->type = AST::AST_NEG; - break; - case vpiPlusOp: - current_node->type = AST::AST_POS; - break; - case vpiPosedgeOp: - current_node->type = AST::AST_POSEDGE; - break; - case vpiNegedgeOp: - current_node->type = AST::AST_NEGEDGE; - break; - case vpiUnaryAndOp: - current_node->type = AST::AST_REDUCE_AND; - break; - case vpiUnaryOrOp: - current_node->type = AST::AST_REDUCE_OR; - break; - case vpiUnaryXorOp: - current_node->type = AST::AST_REDUCE_XOR; - break; - case vpiUnaryXNorOp: - current_node->type = AST::AST_REDUCE_XNOR; - break; - case vpiUnaryNandOp: { - auto not_node = new AST::AstNode(AST::AST_NONE, current_node); - if (current_node->children.size() == 2) { - current_node->type = AST::AST_BIT_AND; - not_node->type = AST::AST_BIT_NOT; - } else { - current_node->type = AST::AST_REDUCE_AND; - not_node->type = AST::AST_LOGIC_NOT; - } - current_node = not_node; - break; - } - case vpiUnaryNorOp: { - auto not_node = new AST::AstNode(AST::AST_NONE, current_node); - if (current_node->children.size() == 2) { - current_node->type = AST::AST_BIT_OR; - not_node->type = AST::AST_BIT_NOT; - } else { - current_node->type = AST::AST_REDUCE_OR; - not_node->type = AST::AST_LOGIC_NOT; - } - current_node = not_node; - break; - } - case vpiBitNegOp: - current_node->type = AST::AST_BIT_NOT; - break; - case vpiBitAndOp: - current_node->type = AST::AST_BIT_AND; - break; - case vpiBitOrOp: - current_node->type = AST::AST_BIT_OR; - break; - case vpiBitXorOp: - current_node->type = AST::AST_BIT_XOR; - break; - case vpiBitXnorOp: - current_node->type = AST::AST_BIT_XNOR; - break; - case vpiLShiftOp: { - current_node->type = AST::AST_SHIFT_LEFT; - log_assert(current_node->children.size() == 2); - auto unsigned_node = new AST::AstNode(AST::AST_TO_UNSIGNED, current_node->children[1]); - current_node->children[1] = unsigned_node; - break; - } - case vpiRShiftOp: { - current_node->type = AST::AST_SHIFT_RIGHT; - log_assert(current_node->children.size() == 2); - auto unsigned_node = new AST::AstNode(AST::AST_TO_UNSIGNED, current_node->children[1]); - current_node->children[1] = unsigned_node; - break; - } - case vpiNotOp: - current_node->type = AST::AST_LOGIC_NOT; - break; - case vpiLogAndOp: - current_node->type = AST::AST_LOGIC_AND; - break; - case vpiLogOrOp: - current_node->type = AST::AST_LOGIC_OR; - break; - case vpiEqOp: - current_node->type = AST::AST_EQ; - break; - case vpiNeqOp: - current_node->type = AST::AST_NE; - break; - case vpiCaseEqOp: - current_node->type = AST::AST_EQX; - break; - case vpiCaseNeqOp: - current_node->type = AST::AST_NEX; - break; - case vpiGtOp: - current_node->type = AST::AST_GT; - break; - case vpiGeOp: - current_node->type = AST::AST_GE; - break; - case vpiLtOp: - current_node->type = AST::AST_LT; - break; - case vpiLeOp: - current_node->type = AST::AST_LE; - break; - case vpiSubOp: - current_node->type = AST::AST_SUB; - if (!current_node->children.empty() && current_node->children[0]->type == AST::AST_LOCALPARAM) { - current_node->children[0]->type = AST::AST_IDENTIFIER; - } - break; - case vpiAddOp: - current_node->type = AST::AST_ADD; - break; - case vpiMultOp: - current_node->type = AST::AST_MUL; - break; - case vpiDivOp: - current_node->type = AST::AST_DIV; - break; - case vpiModOp: - current_node->type = AST::AST_MOD; - break; - case vpiArithLShiftOp: { - current_node->type = AST::AST_SHIFT_SLEFT; - log_assert(current_node->children.size() == 2); - auto unsigned_node = new AST::AstNode(AST::AST_TO_UNSIGNED, current_node->children[1]); - current_node->children[1] = unsigned_node; - break; - } - case vpiArithRShiftOp: { - current_node->type = AST::AST_SHIFT_SRIGHT; - log_assert(current_node->children.size() == 2); - auto unsigned_node = new AST::AstNode(AST::AST_TO_UNSIGNED, current_node->children[1]); - current_node->children[1] = unsigned_node; - break; - } - case vpiPowerOp: - current_node->type = AST::AST_POW; - break; - case vpiPostIncOp: { - // TODO: Make this an actual post-increment op (currently it's a pre-increment) - log_warning("%.*s:%d: Post-incrementation operations are handled as pre-incrementation.\n", (int)object->VpiFile().length(), - object->VpiFile().data(), object->VpiLineNo()); - [[fallthrough]]; - } - case vpiPreIncOp: { - current_node->type = AST::AST_ASSIGN_EQ; - auto id = current_node->children[0]->clone(); - auto add_node = new AST::AstNode(AST::AST_ADD, id, AST::AstNode::mkconst_int(1, true)); - add_node->filename = current_node->filename; - add_node->location = current_node->location; - current_node->children.push_back(add_node); - break; - } - case vpiPostDecOp: { - // TODO: Make this an actual post-decrement op (currently it's a pre-decrement) - log_warning("%.*s:%d: Post-decrementation operations are handled as pre-decrementation.\n", (int)object->VpiFile().length(), - object->VpiFile().data(), object->VpiLineNo()); - [[fallthrough]]; - } - case vpiPreDecOp: { - current_node->type = AST::AST_ASSIGN_EQ; - auto id = current_node->children[0]->clone(); - auto add_node = new AST::AstNode(AST::AST_SUB, id, AST::AstNode::mkconst_int(1, true)); - add_node->filename = current_node->filename; - add_node->location = current_node->location; - current_node->children.push_back(add_node); - break; - } - case vpiConditionOp: - current_node->type = AST::AST_TERNARY; - break; - case vpiConcatOp: { - current_node->type = AST::AST_CONCAT; - std::reverse(current_node->children.begin(), current_node->children.end()); - break; - } - case vpiMultiConcatOp: - case vpiMultiAssignmentPatternOp: - current_node->type = AST::AST_REPLICATE; - break; - case vpiAssignmentOp: - current_node->type = AST::AST_ASSIGN_EQ; - break; - case vpiStreamLROp: { - auto concat_node = current_node->children.back(); - current_node->children.pop_back(); - delete current_node; - current_node = concat_node; - break; - } - case vpiNullOp: { - delete current_node; - current_node = nullptr; - break; - } - case vpiMinTypMaxOp: { - // ignore min and max and set only typ - log_assert(current_node->children.size() == 3); - auto tmp = current_node->children[1]->clone(); - delete current_node; - current_node = tmp; - break; - } - default: { - delete current_node; - current_node = nullptr; - report_error("%.*s:%d: Encountered unhandled operation type %d\n", (int)object->VpiFile().length(), object->VpiFile().data(), - object->VpiLineNo(), operation); - } - } - } - } -} - -void UhdmAst::process_stream_op() -{ - // Closest ancestor where new statements can be inserted. - AST::AstNode *stmt_list_node = find_ancestor({ - AST::AST_MODULE, - AST::AST_PACKAGE, - AST::AST_BLOCK, - AST::AST_INITIAL, - AST::AST_ALWAYS, - AST::AST_FUNCTION, - }); - uhdmast_assert(stmt_list_node != nullptr); - - // Detect whether we're in a procedural context. If yes, `for` loop will be generated, and `generate for` otherwise. - const AST::AstNode *const proc_ctx = find_ancestor({AST::AST_ALWAYS, AST::AST_INITIAL, AST::AST_FUNCTION}); - const bool is_proc_ctx = (proc_ctx != nullptr); - - // Get a prefix for internal identifiers. - const auto stream_op_id = shared.next_loop_id(); - const auto make_id_str = [stream_op_id](const char *suffix) { - return std::string("$systemverilog_plugin$stream_op_") + std::to_string(stream_op_id) + "_" + suffix; - }; - - if (is_proc_ctx) { - // Put logic inside a sub-block to avoid issues with declarations not being at the beginning of a block. - AST::AstNode *block = make_node(Yosys::AST::AST_BLOCK).str(make_id_str("impl")); - stmt_list_node->children.push_back(block); - stmt_list_node = block; - } - - // TODO (mglb): Only concat expression's size factors are supported as a slice size. Add support for other slice sizes as well. - AST::AstNode *slice_size_arg = nullptr; - AST::AstNode *stream_concat_arg = nullptr; - { - std::vector operands; - // Expected operands: [slice_size] stream_concatenation - visit_one_to_many({vpiOperand}, obj_h, [&](AST::AstNode *node) { - uhdmast_assert(node != nullptr); - uhdmast_assert(operands.size() < 2); - operands.push_back(node); - }); - uhdmast_assert(operands.size() > 0); - - if (operands.size() == 2) { - slice_size_arg = operands.at(0); - // SV spec says slice_size can be a constant or a type. However, Surelog converts type to its width, so we always expect a const. - uhdmast_assert(slice_size_arg->type == AST::AST_CONSTANT); - } else { - slice_size_arg = make_const(1u); - } - stream_concat_arg = operands.back(); - } - - AST::AstNode *const stream_concat_width_lp = // - (make_node(AST::AST_LOCALPARAM).str(make_id_str("width")))({ - (make_node(AST::AST_FCALL).str("\\$bits"))({ - (stream_concat_arg->clone()), - }), - (make_range(31, 0, true)), - }); - - // TODO (mglb): src_wire and dst_wire should probably take argument signedness and logicness into account. - AST::AstNode *const src_wire = // - (make_node(AST::AST_WIRE).str(make_id_str("src")).is_reg(is_proc_ctx))({ - (make_node(AST::AST_RANGE))({ - (make_const(0)), - (make_node(AST::AST_SUB))({ - (make_ident(stream_concat_width_lp->str)), - (make_const(1)), - }), - }), - }); - - AST::AstNode *const dst_wire = // - (make_node(AST::AST_WIRE).str(make_id_str("dst")).is_reg(is_proc_ctx))({ - (make_node(AST::AST_RANGE))({ - (make_node(AST::AST_SUB))({ - (make_ident(stream_concat_width_lp->str)), - (make_const(1)), - }), - (make_const(0)), - }), - }); - - AST::AstNode *const assign_stream_concat_to_src_wire = // - (make_node(is_proc_ctx ? AST::AST_ASSIGN_EQ : AST::AST_ASSIGN))({ - (make_ident(src_wire->str)), - (stream_concat_arg), - }); - - AST::AstNode *const loop_counter = // - (make_node(is_proc_ctx ? AST::AST_WIRE : AST::AST_GENVAR).str(make_id_str("counter")).is_reg(true))({ - (make_range(31, 0, true)), - }); - - AST::AstNode *const for_loop = // - (make_node(is_proc_ctx ? AST::AST_FOR : AST::AST_GENFOR))({ - // init statement - (make_node(AST::AST_ASSIGN_EQ))({ - (make_ident(loop_counter->str)), - (make_const(0)), - }), - // condition - (make_node(AST::AST_LT))({ - (make_ident(loop_counter->str)), - (make_ident(stream_concat_width_lp->str)), - }), - // iteration expression - (make_node(AST::AST_ASSIGN_EQ))({ - (make_ident(loop_counter->str)), - (make_node(Yosys::AST::AST_ADD))({ - (make_ident(loop_counter->str)), - (slice_size_arg->clone()), - }), - }), - // loop body - (make_node(is_proc_ctx ? AST::AST_BLOCK : AST::AST_GENBLOCK).str(make_id_str("loop_body")))({ - (make_node(is_proc_ctx ? AST::AST_ASSIGN_EQ : AST::AST_ASSIGN))({ - (make_ident(dst_wire->str))({ - (make_node(AST::AST_RANGE))({ - (make_node(Yosys::AST::AST_SUB))({ - (make_node(Yosys::AST::AST_ADD))({ - (make_node(Yosys::AST::AST_SELFSZ))({ - (make_ident(loop_counter->str)), - }), - (slice_size_arg->clone()), - }), - (make_const(1)), - }), - (make_node(Yosys::AST::AST_ADD))({ - (make_node(Yosys::AST::AST_SELFSZ))({ - (make_ident(loop_counter->str)), - }), - (make_const(0)), - }), - }), - }), - (make_ident(src_wire->str))({ - (make_node(AST::AST_RANGE))({ - (make_node(Yosys::AST::AST_SUB))({ - (make_node(Yosys::AST::AST_ADD))({ - (make_node(Yosys::AST::AST_SELFSZ))({ - (make_ident(loop_counter->str)), - }), - (slice_size_arg), - }), - (make_const(1)), - }), - (make_node(Yosys::AST::AST_ADD))({ - (make_node(Yosys::AST::AST_SELFSZ))({ - (make_ident(loop_counter->str)), - }), - (make_const(0)), - }), - }), - }), - }), - }), - }); - - stmt_list_node->children.insert(stmt_list_node->children.end(), { - stream_concat_width_lp, - src_wire, - dst_wire, - assign_stream_concat_to_src_wire, - loop_counter, - for_loop, - }); - - current_node = make_ident(is_proc_ctx ? (stmt_list_node->str + '.' + dst_wire->str) : dst_wire->str); -} - -void UhdmAst::process_list_op() -{ - // Add all operands as children of process node - if (auto parent_node = find_ancestor({AST::AST_ALWAYS, AST::AST_COND})) { - visit_one_to_many({vpiOperand}, obj_h, [&](AST::AstNode *node) { - // add directly to process/cond node - if (node) { - parent_node->children.push_back(node); - } - }); - } else { - log_error("Unhandled list op, couldn't find parent node."); - } - // Do not create a node -} - -void UhdmAst::process_cast_op() -{ - current_node = make_ast_node(AST::AST_NONE); - visit_one_to_many({vpiOperand}, obj_h, [&](AST::AstNode *node) { - node->cloneInto(current_node); - delete node; - }); - vpiHandle typespec_h = vpi_handle(vpiTypespec, obj_h); - vpi_release_handle(typespec_h); -} - -void UhdmAst::process_inside_op() -{ - current_node = make_ast_node(AST::AST_EQ); - AST::AstNode *lhs = nullptr; - visit_one_to_many({vpiOperand}, obj_h, [&](AST::AstNode *node) { - if (!lhs) { - lhs = node; - } - if (current_node->children.size() < 2) { - current_node->children.push_back(node); - } else { - auto or_node = new AST::AstNode(AST::AST_LOGIC_OR); - or_node->filename = current_node->filename; - or_node->location = current_node->location; - auto eq_node = new AST::AstNode(AST::AST_EQ); - eq_node->filename = current_node->filename; - eq_node->location = current_node->location; - or_node->children.push_back(current_node); - or_node->children.push_back(eq_node); - eq_node->children.push_back(lhs->clone()); - eq_node->children.push_back(node); - current_node = or_node; - } - }); -} - -void UhdmAst::process_assignment_pattern_op() -{ - current_node = make_ast_node(AST::AST_CONCAT); - if (auto param_node = find_ancestor({AST::AST_PARAMETER, AST::AST_LOCALPARAM})) { - std::map ordered_children; - visit_one_to_many({vpiOperand}, obj_h, [&](AST::AstNode *node) { - if (node->type == AST::AST_ASSIGN || node->type == AST::AST_ASSIGN_EQ || node->type == AST::AST_ASSIGN_LE) { - // Get the name of the parameter or it's child, to which the pattern is assigned. - std::string key; - if (!node->children.empty() && !node->children[0]->children.empty() && - node->children[0]->children[0]->type == static_cast(AST::Extended::AST_DOT)) { - key = node->children[0]->children[0]->str; - } else if (!node->children.empty()) { - key = node->children[0]->str; - } else { - log_file_error(node->filename, node->location.first_line, "Couldn't find `key` in assignment pattern.\n"); - } - auto param_type = shared.param_types[param_node->str]; - if (!param_type) { - log_error("Couldn't find parameter type for node: %s\n", param_node->str.c_str()); - } - // Place the child node holding the value assigned in the pattern, in the right order, - // so the overall value of the param_node is correct. - size_t pos = - std::find_if(param_type->children.begin(), param_type->children.end(), [key](AST::AstNode *child) { return child->str == key; }) - - param_type->children.begin(); - ordered_children.insert(std::make_pair(pos, node->children[1]->clone())); - delete node; - } else { - current_node->children.push_back(node); - } - }); - for (auto p : ordered_children) { - current_node->children.push_back(p.second); - } - std::reverse(current_node->children.begin(), current_node->children.end()); - return; - } - auto assign_node = find_ancestor({AST::AST_ASSIGN, AST::AST_ASSIGN_EQ, AST::AST_ASSIGN_LE}); - - auto proc_node = - find_ancestor({AST::AST_BLOCK, AST::AST_GENBLOCK, AST::AST_ALWAYS, AST::AST_INITIAL, AST::AST_MODULE, AST::AST_PACKAGE, AST::AST_CELL}); - if (proc_node && proc_node->type == AST::AST_CELL && shared.top_nodes.count(proc_node->children[0]->str)) { - proc_node = shared.top_nodes[proc_node->children[0]->str]; - } - std::vector assignments; - visit_one_to_many({vpiOperand}, obj_h, [&](AST::AstNode *node) { - if (node->type == AST::AST_ASSIGN || node->type == AST::AST_ASSIGN_EQ || node->type == AST::AST_ASSIGN_LE) { - assignments.push_back(node); - } else { - current_node->children.push_back(node); - } - }); - std::reverse(current_node->children.begin(), current_node->children.end()); - if (!assignments.empty()) { - if (current_node->children.empty()) { - delete assign_node->children[0]; - assign_node->children[0] = assignments[0]->children[0]; - delete current_node; - current_node = assignments[0]->children[1]; - assignments[0]->children.clear(); - delete assignments[0]; - proc_node->children.insert(proc_node->children.end(), assignments.begin() + 1, assignments.end()); - } else { - proc_node->children.insert(proc_node->children.end(), assignments.begin(), assignments.end()); - } - } -} - -void UhdmAst::process_bit_select() -{ - current_node = make_ast_node(AST::AST_IDENTIFIER); - visit_one_to_one({vpiIndex}, obj_h, [&](AST::AstNode *node) { current_node->children.push_back(make_node(AST::AST_RANGE)({node})); }); -} - -void UhdmAst::process_part_select() -{ - current_node = make_ast_node(AST::AST_IDENTIFIER); - AST::AstNode *range_node = make_node(AST::AST_RANGE); - visit_one_to_one({vpiLeftRange, vpiRightRange}, obj_h, [&](AST::AstNode *node) { range_node->children.push_back(node); }); - current_node->children.push_back(range_node); -} - -void UhdmAst::process_indexed_part_select() -{ - current_node = make_ast_node(AST::AST_IDENTIFIER); - // TODO: check if there are other types, for now only handle 1 and 2 (+: and -:) - auto indexed_part_select_type = vpi_get(vpiIndexedPartSelectType, obj_h) == 1 ? AST::AST_ADD : AST::AST_SUB; - AST::AstNode *range_node = make_node(AST::AST_RANGE); - visit_one_to_one({vpiBaseExpr}, obj_h, [&](AST::AstNode *node) { range_node->children.push_back(node); }); - visit_one_to_one({vpiWidthExpr}, obj_h, [&](AST::AstNode *node) { - AST::AstNode *right_range_node = make_node(indexed_part_select_type); - right_range_node->children.push_back(range_node->children[0]->clone()); - right_range_node->children.push_back(node); - AST::AstNode *sub = make_node(indexed_part_select_type == AST::AST_ADD ? AST::AST_SUB : AST::AST_ADD); - sub->children.push_back(right_range_node); - sub->children.push_back(AST::AstNode::mkconst_int(1, false, 1)); - range_node->children.push_back(sub); - }); - if (indexed_part_select_type == AST::AST_ADD) { - std::reverse(range_node->children.begin(), range_node->children.end()); - } - current_node->children.push_back(range_node); -} - -void UhdmAst::process_if_else() -{ - current_node = make_ast_node(AST::AST_CASE); - visit_one_to_one({vpiCondition}, obj_h, [&](AST::AstNode *node) { - if (!node) { - log_error("Couldn't find node in if stmt. This can happend if unsupported '$value$plusargs' function is used inside if.\n"); - } - auto reduce_node = new AST::AstNode(AST::AST_REDUCE_BOOL, node); - current_node->children.push_back(reduce_node); - }); - // If true: - auto *condition = new AST::AstNode(AST::AST_COND); - auto *constant = AST::AstNode::mkconst_int(1, false, 1); - condition->children.push_back(constant); - visit_one_to_one({vpiStmt}, obj_h, [&](AST::AstNode *node) { - auto *statements = new AST::AstNode(AST::AST_BLOCK); - if (node) - statements->children.push_back(node); - condition->children.push_back(statements); - }); - current_node->children.push_back(condition); - // Else: - if (vpi_get(vpiType, obj_h) == vpiIfElse) { - auto *condition = new AST::AstNode(AST::AST_COND); - auto *elseBlock = new AST::AstNode(AST::AST_DEFAULT); - condition->children.push_back(elseBlock); - visit_one_to_one({vpiElseStmt}, obj_h, [&](AST::AstNode *node) { - auto *statements = new AST::AstNode(AST::AST_BLOCK); - if (node) - statements->children.push_back(node); - condition->children.push_back(statements); - }); - current_node->children.push_back(condition); - } -} - -void UhdmAst::process_for() -{ - current_node = make_ast_node(AST::AST_BLOCK); - auto loop_id = shared.next_loop_id(); - current_node->str = "$fordecl_block" + std::to_string(loop_id); - auto loop = make_ast_node(AST::AST_FOR); - loop->str = "$loop" + std::to_string(loop_id); - visit_one_to_many({vpiForInitStmt}, obj_h, [&](AST::AstNode *node) { - if (node->type == AST::AST_ASSIGN_LE) - node->type = AST::AST_ASSIGN_EQ; - auto lhs = node->children[0]; - if (lhs->type == AST::AST_WIRE) { - auto *wire = lhs->clone(); - wire->is_logic = true; - current_node->children.push_back(wire); - lhs->type = AST::AST_IDENTIFIER; - lhs->is_signed = false; - lhs->delete_children(); - } - loop->children.push_back(node); - }); - visit_one_to_one({vpiCondition}, obj_h, [&](AST::AstNode *node) { loop->children.push_back(node); }); - visit_one_to_many({vpiForIncStmt}, obj_h, [&](AST::AstNode *node) { - if (node->type == AST::AST_ASSIGN_LE) - node->type = AST::AST_ASSIGN_EQ; - loop->children.push_back(node); - }); - visit_one_to_one({vpiStmt}, obj_h, [&](AST::AstNode *node) { - if (node->type != AST::AST_BLOCK) { - auto *statements = make_ast_node(AST::AST_BLOCK); - statements->str = current_node->str; // Needed in simplify step - statements->children.push_back(node); - loop->children.push_back(statements); - } else { - if (node->str == "") { - node->str = loop->str; - } - loop->children.push_back(node); - } - }); - current_node->children.push_back(loop); - transform_breaks_continues(loop, current_node); -} - -void UhdmAst::process_gen_scope() -{ - current_node = make_ast_node(AST::AST_GENBLOCK); - visit_one_to_many({vpiTypedef}, obj_h, [&](AST::AstNode *node) { - if (node) { - move_type_to_new_typedef(current_node, node); - } - }); - - visit_one_to_many( - {vpiParameter, vpiParamAssign, vpiNet, vpiArrayNet, vpiVariables, vpiContAssign, vpiProcess, vpiModule, vpiGenScopeArray, vpiTaskFunc}, obj_h, - [&](AST::AstNode *node) { - if (node) { - if (get_attribute(node, attr_id::is_type_parameter)) { - // Don't process type parameters. - delete node; - return; - } - add_or_replace_child(current_node, node); - } - }); -} - -void UhdmAst::process_case() -{ - current_node = make_ast_node(AST::AST_CASE); - visit_one_to_one({vpiCondition}, obj_h, [&](AST::AstNode *node) { current_node->children.push_back(node); }); - visit_one_to_many({vpiCaseItem}, obj_h, [&](AST::AstNode *node) { current_node->children.push_back(node); }); -} - -void UhdmAst::process_case_item() -{ - auto cond_type = AST::AST_COND; - if (vpiHandle parent_h = vpi_handle(vpiParent, obj_h)) { - switch (vpi_get(vpiCaseType, parent_h)) { - case vpiCaseExact: - cond_type = AST::AST_COND; - break; - case vpiCaseX: - cond_type = AST::AST_CONDX; - break; - case vpiCaseZ: - cond_type = AST::AST_CONDZ; - break; - default: { - const uhdm_handle *const handle = (const uhdm_handle *)obj_h; - const UHDM::BaseClass *const object = (const UHDM::BaseClass *)handle->object; - report_error("%.*s:%d: Unknown case type", (int)object->VpiFile().length(), object->VpiFile().data(), object->VpiLineNo()); - } - } - vpi_release_handle(parent_h); - } - current_node = make_ast_node(cond_type); - vpiHandle itr = vpi_iterate(vpiExpr, obj_h); - while (vpiHandle expr_h = vpi_scan(itr)) { - // case ... inside statement, the operation is stored in UHDM inside case items - // Retrieve just the InsideOp arguments here, we don't add any special handling - if (vpi_get(vpiType, expr_h) == vpiOperation && vpi_get(vpiOpType, expr_h) == vpiInsideOp) { - visit_one_to_many({vpiOperand}, expr_h, [&](AST::AstNode *node) { - // Currently we are adding nodes directly to ancestor - // inside process_list_op, so after this function, we have - // nodes already in `current_node`. - // We should probably refactor this to return node instead. - // For now, make sure this function doesn't return any nodes. - log_assert(node == nullptr); - }); - // vpiListOp is returned in 2 cases: - // a, b, c ... -> multiple vpiListOp with single item - // [a : b] -> single vpiListOp with 2 items - // single item is handled by default, - // here handle 2 items with custom low_high_bound attribute - if (current_node->children.size() == 2) { - auto block = make_ast_node(AST::AST_BLOCK); - block->children = std::move(current_node->children); - current_node->children.clear(); - current_node->children.push_back(block); - current_node->attributes[UhdmAst::low_high_bound()] = AST::AstNode::mkconst_int(1, false, 1); - } - } else { - UhdmAst uhdm_ast(this, shared, indent + " "); - auto *node = uhdm_ast.process_object(expr_h); - if (node) { - current_node->children.push_back(node); - } - } - vpi_release_handle(expr_h); - } - vpi_release_handle(itr); - if (current_node->children.empty()) { - current_node->children.push_back(new AST::AstNode(AST::AST_DEFAULT)); - } - visit_one_to_one({vpiStmt}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (node->type != AST::AST_BLOCK) { - auto block_node = new AST::AstNode(AST::AST_BLOCK); - block_node->children.push_back(node); - node = block_node; - } - current_node->children.push_back(node); - } - }); -} - -void UhdmAst::process_range(const UHDM::BaseClass *object) -{ - current_node = make_ast_node(AST::AST_RANGE); - visit_one_to_one({vpiLeftRange, vpiRightRange}, obj_h, [&](AST::AstNode *node) { current_node->children.push_back(node); }); - if (current_node->children.size() > 0) { - if (current_node->children[0]->str == "unsized") { - log_error("%.*s:%d: Currently not supported object of type 'unsized range'\n", (int)object->VpiFile().length(), object->VpiFile().data(), - object->VpiLineNo()); - } - } - if (current_node->children.size() > 1) { - if (current_node->children[1]->str == "unsized") { - log_error("%.*s:%d: Currently not supported object of type 'unsized range'\n", (int)object->VpiFile().length(), object->VpiFile().data(), - object->VpiLineNo()); - } - } -} - -void UhdmAst::process_return() -{ - current_node = make_ast_node(AST::AST_ASSIGN_EQ); - auto func_node = find_ancestor({AST::AST_FUNCTION, AST::AST_TASK}); - if (!func_node->children.empty()) { - auto lhs = new AST::AstNode(AST::AST_IDENTIFIER); - lhs->str = func_node->children[0]->str; - current_node->children.push_back(lhs); - } - visit_one_to_one({vpiCondition}, obj_h, [&](AST::AstNode *node) { current_node->children.push_back(node); }); -} - -void UhdmAst::process_function() -{ - current_node = make_ast_node(vpi_get(vpiType, obj_h) == vpiFunction ? AST::AST_FUNCTION : AST::AST_TASK); - visit_one_to_one({vpiReturn}, obj_h, [&](AST::AstNode *node) { - if (node) { - auto net_type = vpi_get(vpiNetType, obj_h); - node->is_reg = net_type == vpiReg; - node->str = current_node->str; - current_node->children.push_back(node); - } - }); - visit_one_to_many({vpiParameter, vpiParamAssign}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (get_attribute(node, attr_id::is_type_parameter)) { - // Don't process type parameters. - delete node; - return; - } - add_or_replace_child(current_node, node); - } - }); - visit_one_to_many({vpiIODecl}, obj_h, [&](AST::AstNode *node) { - node->type = AST::AST_WIRE; - node->port_id = shared.next_port_id(); - current_node->children.push_back(node); - }); - visit_one_to_many({vpiVariables}, obj_h, [&](AST::AstNode *node) { current_node->children.push_back(node); }); - visit_one_to_one({vpiStmt}, obj_h, [&](AST::AstNode *node) { - if (node) { - current_node->children.push_back(node); - } - }); -} - -void UhdmAst::process_hier_path() -{ - AST::AstNode *top_node = nullptr; - visit_one_to_many({vpiActual}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (node->str.find('[') != std::string::npos) - node->str = node->str.substr(0, node->str.find('[')); - // for first node, just set correct string and move any children - if (!top_node) { - current_node = node; - top_node = current_node; - } else { - if (node->type == AST::AST_IDENTIFIER && !node->str.empty()) { - node->type = static_cast(AST::Extended::AST_DOT); - top_node->children.push_back(node); - top_node = node; - } else { - top_node->children.push_back(node->children[0]); - node->children.erase(node->children.begin()); - delete node; - } - } - } - }); -} - -void UhdmAst::process_gen_scope_array() -{ - current_node = make_ast_node(AST::AST_GENBLOCK); - visit_one_to_many({vpiGenScope}, obj_h, [&](AST::AstNode *genscope_node) { - for (auto *child : genscope_node->children) { - if (child->type == AST::AST_PARAMETER || child->type == AST::AST_LOCALPARAM) { - auto param_str = child->str.substr(1); - auto array_str = "[" + param_str + "]"; - visitEachDescendant(genscope_node, [&](AST::AstNode *node) { - auto pos = node->str.find(array_str); - if (pos != std::string::npos) { - node->type = AST::AST_PREFIX; - auto *param = new AST::AstNode(AST::AST_IDENTIFIER); - param->str = child->str; - node->children.push_back(param); - auto bracket = node->str.rfind(']'); - if (bracket + 2 <= node->str.size()) { - auto *field = new AST::AstNode(AST::AST_IDENTIFIER); - field->str = "\\" + node->str.substr(bracket + 2); - node->children.push_back(field); - } - node->str = node->str.substr(0, node->str.find('[')); - } - }); - } - } - current_node->children.insert(current_node->children.end(), genscope_node->children.begin(), genscope_node->children.end()); - genscope_node->children.clear(); - delete genscope_node; - }); -} - -void UhdmAst::process_tagged_pattern() -{ - auto assign_node = find_ancestor({AST::AST_ASSIGN, AST::AST_ASSIGN_EQ, AST::AST_ASSIGN_LE}); - auto assign_type = AST::AST_ASSIGN; - AST::AstNode *lhs_node = nullptr; - if (assign_node) { - assign_type = assign_node->type; - lhs_node = assign_node->children[0]->clone(); - } else { - lhs_node = new AST::AstNode(AST::AST_IDENTIFIER); - auto ancestor = find_ancestor({AST::AST_WIRE, AST::AST_MEMORY, AST::AST_PARAMETER, AST::AST_LOCALPARAM}); - if (!ancestor) { - const uhdm_handle *const handle = (const uhdm_handle *)obj_h; - const UHDM::BaseClass *const object = (const UHDM::BaseClass *)handle->object; - report_error("%.*s:%d: Couldn't find ancestor for tagged pattern!\n", (int)object->VpiFile().length(), object->VpiFile().data(), - object->VpiLineNo()); - } - lhs_node->str = ancestor->str; - } - current_node = new AST::AstNode(assign_type); - current_node->children.push_back(lhs_node); - auto typespec_h = vpi_handle(vpiTypespec, obj_h); - 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::Extended::AST_DOT)); - field->str = field_name; - current_node->children[0]->children.push_back(field); - } - } else if (vpi_get(vpiType, typespec_h) == vpiIntegerTypespec) { - s_vpi_value val; - vpi_get_value(typespec_h, &val); - auto range = new AST::AstNode(AST::AST_RANGE); - auto index = AST::AstNode::mkconst_int(val.value.integer, false); - range->children.push_back(index); - current_node->children[0]->children.push_back(range); - } - vpi_release_handle(typespec_h); - visit_one_to_one({vpiPattern}, obj_h, [&](AST::AstNode *node) { current_node->children.push_back(node); }); -} - -void UhdmAst::process_logic_var() -{ - current_node = make_ast_node(AST::AST_WIRE); - current_node->is_logic = true; - std::vector packed_ranges; // comes before wire name - std::vector unpacked_ranges; // comes after wire name - // TODO: add const attribute, but it seems it is little more - // then just setting boolean value - // current_node->is_const = vpi_get(vpiConstantVariable, obj_h); - visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) { - if (node->str.empty()) { - // anonymous typespec, move the children to variable - current_node->type = node->type; - current_node->children = std::move(node->children); - } else { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - current_node->is_custom_type = true; - } - current_node->is_signed = node->is_signed; - delete node; - }); - // TODO: Handling below seems similar to other typespec accesses for range. Candidate for extraction to a function. - if (auto typespec_h = vpi_handle(vpiTypespec, obj_h)) { - visit_one_to_many({vpiRange}, typespec_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - vpi_release_handle(typespec_h); - } else { - visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - } - visit_default_expr(obj_h); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); -} - -void UhdmAst::process_sys_func_call() -{ - current_node = make_ast_node(AST::AST_FCALL); - - std::string task_calls[] = {"\\$display", "\\$monitor", "\\$write", "\\$time", "\\$readmemh", "\\$readmemb", "\\$finish", "\\$stop"}; - - if (current_node->str == "\\$signed") { - current_node->type = AST::AST_TO_SIGNED; - } else if (current_node->str == "\\$unsigned") { - current_node->type = AST::AST_TO_UNSIGNED; - } else if (std::find(std::begin(task_calls), std::end(task_calls), current_node->str) != std::end(task_calls)) { - current_node->type = AST::AST_TCALL; - } - - visit_one_to_many({vpiArgument}, obj_h, [&](AST::AstNode *node) { - if (node) { - current_node->children.push_back(node); - } - }); - - if (current_node->str == "\\$display" || current_node->str == "\\$write") { - // According to standard, %h and %x mean the same, but %h is currently unsupported by mainline yosys - std::string replaced_string = std::regex_replace(current_node->children[0]->str, std::regex("%[h|H]"), "%x"); - delete current_node->children[0]; - current_node->children[0] = AST::AstNode::mkconst_str(replaced_string); - } - - std::string remove_backslash[] = {"\\$display", "\\$strobe", "\\$write", "\\$monitor", "\\$time", "\\$finish", - "\\$stop", "\\$dumpfile", "\\$dumpvars", "\\$dumpon", "\\$dumpoff", "\\$dumpall"}; - - if (std::find(std::begin(remove_backslash), std::end(remove_backslash), current_node->str) != std::end(remove_backslash)) - current_node->str = current_node->str.substr(1); -} - -void UhdmAst::process_tf_call(AST::AstNodeType type) -{ - current_node = make_ast_node(type); - visit_one_to_many({vpiArgument}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (node->type == AST::AST_PARAMETER || node->type == AST::AST_LOCALPARAM) { - node->type = AST::AST_IDENTIFIER; - node->children.clear(); - } - current_node->children.push_back(node); - } - }); - - // Calls to functions imported from packages do not contain package name in vpiName. A full function name, containing package name, - // is necessary e.g. when call to a function is used as a value assigned to a port of a module instantiated inside generate for loop. - // However, we can't use full function name when it refers to a module's local function. - // To make it work the called function name is used instead of vpiName from the call object only when it contains package name (detected here - // by presence of "::"). - // TODO(mglb): This can fail when "::" is just a part of an escaped identifier. Handle such cases properly here and in other places. - const uhdm_handle *const handle = (const uhdm_handle *)obj_h; - if (handle->type == UHDM::uhdmfunc_call) { - const auto *const base_object = (const UHDM::BaseClass *)handle->object; - const auto *const fcall = base_object->Cast(); - if (fcall->Function()) { - auto fname = fcall->Function()->VpiFullName(); - if (fname.find("::") != std::string_view::npos) { - current_node->str = fname; - sanitize_symbol_name(current_node->str); - } - } - } -} - -void UhdmAst::process_immediate_assert() -{ - current_node = make_ast_node(AST::AST_ASSERT); - visit_one_to_one({vpiExpr}, obj_h, [&](AST::AstNode *n) { - if (n) { - current_node->children.push_back(n); - } - }); -} - -void UhdmAst::process_logic_typespec() -{ - current_node = make_ast_node(AST::AST_WIRE); - current_node->is_logic = true; - std::vector packed_ranges; // comes before wire name - std::vector unpacked_ranges; // comes after wire name - if (!current_node->str.empty() && current_node->str.find("::") == std::string::npos) { - std::string package_name = ""; - if (vpiHandle instance_h = vpi_handle(vpiInstance, obj_h)) { - if (vpi_get(vpiType, instance_h) == vpiPackage) { - package_name = get_object_name(instance_h, {vpiDefName}); - current_node->str = package_name + "::" + current_node->str.substr(1); - } - vpi_release_handle(instance_h); - } - } - visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - if (packed_ranges.empty()) - packed_ranges.push_back(make_range(0, 0)); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); - current_node->is_signed = vpi_get(vpiSigned, obj_h); -} - -void UhdmAst::process_int_typespec() -{ - std::vector packed_ranges; // comes before wire name - std::vector unpacked_ranges; // comes after wire name - current_node = make_ast_node(AST::AST_WIRE); - packed_ranges.push_back(make_range(31, 0)); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); - current_node->is_signed = vpi_get(vpiSigned, obj_h); -} - -void UhdmAst::process_shortint_typespec() -{ - std::vector packed_ranges; // comes before wire name - std::vector unpacked_ranges; // comes after wire name - current_node = make_ast_node(AST::AST_WIRE); - packed_ranges.push_back(make_range(15, 0)); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); - current_node->is_signed = vpi_get(vpiSigned, obj_h); -} - -void UhdmAst::process_longint_typespec() -{ - std::vector packed_ranges; // comes before wire name - std::vector unpacked_ranges; // comes after wire name - current_node = make_ast_node(AST::AST_WIRE); - packed_ranges.push_back(make_range(63, 0)); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); - current_node->is_signed = vpi_get(vpiSigned, obj_h); -} - -void UhdmAst::process_byte_typespec() -{ - std::vector packed_ranges; // comes before wire name - std::vector unpacked_ranges; // comes after wire name - current_node = make_ast_node(AST::AST_WIRE); - packed_ranges.push_back(make_range(7, 0)); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); - current_node->is_signed = vpi_get(vpiSigned, obj_h); -} - -void UhdmAst::process_time_typespec() -{ - std::vector packed_ranges; // comes before wire name - std::vector unpacked_ranges; // comes after wire name - current_node = make_ast_node(AST::AST_WIRE); - packed_ranges.push_back(make_range(63, 0)); - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); - current_node->is_signed = false; -} - -void UhdmAst::process_string_var() -{ - current_node = make_ast_node(AST::AST_WIRE); - current_node->is_string = true; - // FIXME: - // this is only basic support for strings, - // currently yosys doesn't support dynamic resize of wire - // based on string size - // here we try to get size of string based on provided const string - // if it is not available, we are setting size to explicite 64 bits - visit_one_to_one({vpiExpr}, obj_h, [&](AST::AstNode *expr_node) { - if (expr_node->type == AST::AST_CONSTANT) { - auto left_const = AST::AstNode::mkconst_int(expr_node->range_left, true); - auto right_const = AST::AstNode::mkconst_int(expr_node->range_right, true); - auto range = make_ast_node(AST::AST_RANGE, {left_const, right_const}); - current_node->children.push_back(range); - } - }); - if (current_node->children.empty()) { - auto left_const = AST::AstNode::mkconst_int(64, true); - auto right_const = AST::AstNode::mkconst_int(0, true); - auto range = make_ast_node(AST::AST_RANGE, {left_const, right_const}); - current_node->children.push_back(range); - } - visit_default_expr(obj_h); -} - -void UhdmAst::process_string_typespec() -{ - current_node = make_ast_node(AST::AST_WIRE); - current_node->is_string = true; - // FIXME: - // this is only basic support for strings, - // currently yosys doesn't support dynamic resize of wire - // based on string size - // here, we are setting size to explicite 64 bits - auto left_const = AST::AstNode::mkconst_int(64, true); - auto right_const = AST::AstNode::mkconst_int(0, true); - auto range = make_ast_node(AST::AST_RANGE, {left_const, right_const}); - current_node->children.push_back(range); -} - -void UhdmAst::process_bit_typespec() -{ - current_node = make_ast_node(AST::AST_WIRE); - visit_range(obj_h, [&](AST::AstNode *node) { - if (node) { - current_node->children.push_back(node); - } - }); - current_node->is_signed = vpi_get(vpiSigned, obj_h); -} - -void UhdmAst::process_repeat() -{ - auto loop_id = shared.next_loop_id(); - current_node = make_ast_node(AST::AST_BLOCK); - current_node->str = "$repeatdecl_block" + std::to_string(loop_id); - auto *loop = make_ast_node(AST::AST_REPEAT); - loop->str = "$loop" + std::to_string(loop_id); - current_node->children.push_back(loop); - visit_one_to_one({vpiCondition}, obj_h, [&](AST::AstNode *node) { loop->children.push_back(node); }); - visit_one_to_one({vpiStmt}, obj_h, [&](AST::AstNode *node) { - if (node->type != AST::AST_BLOCK) { - node = new AST::AstNode(AST::AST_BLOCK, node); - } - if (node->str.empty()) { - node->str = loop->str; // Needed in simplify step - } - loop->children.push_back(node); - }); - transform_breaks_continues(loop, current_node); -} - -void UhdmAst::process_var_select() -{ - current_node = make_ast_node(AST::AST_IDENTIFIER); - visit_one_to_many({vpiIndex}, obj_h, [&](AST::AstNode *node) { - if (node->str == current_node->str) { - for (auto child : node->children) { - current_node->children.push_back(child); - } - node->children.clear(); - delete node; - } else { - auto range_node = new AST::AstNode(AST::AST_RANGE); - range_node->filename = current_node->filename; - range_node->location = current_node->location; - range_node->children.push_back(node); - current_node->children.push_back(range_node); - } - }); -} - -void UhdmAst::process_port() -{ - current_node = make_ast_node(AST::AST_WIRE); - current_node->port_id = shared.next_port_id(); - vpiHandle lowConn_h = vpi_handle(vpiLowConn, obj_h); - std::vector packed_ranges; // comes before wire name - std::vector unpacked_ranges; // comes after wire name - if (lowConn_h) { - vpiHandle actual_h = vpi_handle(vpiActual, lowConn_h); - auto actual_type = vpi_get(vpiType, actual_h); - switch (actual_type) { - case vpiModport: { - vpiHandle iface_h = vpi_handle(vpiInterface, actual_h); - if (iface_h) { - std::string cellName, ifaceName; - if (auto s = vpi_get_str(vpiName, actual_h)) { - cellName = s; - sanitize_symbol_name(cellName); - } - if (auto s = vpi_get_str(vpiDefName, iface_h)) { - ifaceName = s; - sanitize_symbol_name(ifaceName); - } - current_node->type = AST::AST_INTERFACEPORT; - auto typeNode = new AST::AstNode(AST::AST_INTERFACEPORTTYPE); - // Skip '\' in cellName - typeNode->str = ifaceName + '.' + cellName.substr(1, cellName.length()); - current_node->children.push_back(typeNode); - vpi_release_handle(iface_h); - } - break; - } - case vpiInterface: { - auto typeNode = new AST::AstNode(AST::AST_INTERFACEPORTTYPE); - if (auto s = vpi_get_str(vpiDefName, actual_h)) { - typeNode->str = s; - sanitize_symbol_name(typeNode->str); - } - current_node->type = AST::AST_INTERFACEPORT; - current_node->children.push_back(typeNode); - break; - } - case vpiLogicVar: - case vpiLogicNet: { - current_node->is_logic = true; - current_node->is_signed = vpi_get(vpiSigned, actual_h); - visit_one_to_many({vpiRange}, actual_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - break; - } - case vpiPackedArrayVar: - visit_one_to_many({vpiElement}, actual_h, [&](AST::AstNode *node) { - if (node && GetSize(node->children) == 1) { - current_node->children.push_back(node->children[0]->clone()); - if (node->children[0]->type == AST::AST_WIRETYPE) { - current_node->is_custom_type = true; - } - } - delete node; - }); - visit_one_to_many({vpiRange}, actual_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - break; - case vpiPackedArrayNet: - visit_one_to_many({vpiRange}, actual_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - break; - case vpiArrayVar: - visit_one_to_many({vpiRange}, actual_h, [&](AST::AstNode *node) { unpacked_ranges.push_back(node); }); - break; - case vpiEnumNet: - case vpiStructNet: - case vpiArrayNet: - case vpiStructVar: - case vpiUnionVar: - case vpiEnumVar: - case vpiBitVar: - case vpiByteVar: - case vpiShortIntVar: - case vpiLongIntVar: - case vpiIntVar: - case vpiIntegerVar: - break; - default: { - const uhdm_handle *const handle = (const uhdm_handle *)actual_h; - const UHDM::BaseClass *const object = (const UHDM::BaseClass *)handle->object; - report_error("%.*s:%d: Encountered unhandled type in process_port: %s\n", (int)object->VpiFile().length(), object->VpiFile().data(), - object->VpiLineNo(), UHDM::VpiTypeName(actual_h).c_str()); - break; - } - } - vpi_release_handle(actual_h); - vpi_release_handle(lowConn_h); - } - visit_one_to_one({vpiTypedef}, obj_h, [&](AST::AstNode *node) { - if (node) { - if (current_node->children.empty() || current_node->children[0]->type != AST::AST_WIRETYPE) { - if (!node->str.empty()) { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - // wiretype needs to be 1st node (if port have also another range nodes) - current_node->children.insert(current_node->children.begin(), wiretype_node); - current_node->is_custom_type = true; - } else { - // anonymous typedef, just move children - current_node->children = std::move(node->children); - } - } - current_node->is_signed = current_node->is_signed || node->is_signed; - delete node; - } - }); - if (const int n = vpi_get(vpiDirection, obj_h)) { - if (n == vpiInput) { - current_node->is_input = true; - } else if (n == vpiOutput) { - current_node->is_output = true; - } else if (n == vpiInout) { - current_node->is_input = true; - current_node->is_output = true; - } - } - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); -} - -void UhdmAst::process_net() -{ - current_node = make_ast_node(AST::AST_WIRE); - auto net_type = vpi_get(vpiNetType, obj_h); - current_node->is_reg = net_type == vpiReg; - current_node->is_output = net_type == vpiOutput; - current_node->is_logic = !current_node->is_reg; - current_node->is_signed = vpi_get(vpiSigned, obj_h); - visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) { - if (!node) - return; - if (!node->str.empty()) { - auto wiretype_node = new AST::AstNode(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - // wiretype needs to be 1st node - current_node->children.insert(current_node->children.begin(), wiretype_node); - current_node->is_custom_type = true; - } else { - // Ranges from the typespec are copied to the current node as attributes. - // So that multiranges can be replaced with a single range as a node later. - copy_packed_unpacked_attribute(node, current_node); - } - delete node; - }); -} - -void UhdmAst::process_parameter() -{ - auto type = vpi_get(vpiLocalParam, obj_h) == 1 ? AST::AST_LOCALPARAM : AST::AST_PARAMETER; - current_node = make_ast_node(type); - std::vector packed_ranges; // comes before wire name - std::vector unpacked_ranges; // comes after wire name - visit_one_to_many({vpiRange}, obj_h, [&](AST::AstNode *node) { unpacked_ranges.push_back(node); }); - vpiHandle typespec_h = vpi_handle(vpiTypespec, obj_h); - if (typespec_h) { - int typespec_type = vpi_get(vpiType, typespec_h); - switch (typespec_type) { - case vpiBitTypespec: - case vpiLogicTypespec: { - current_node->is_logic = true; - visit_one_to_many({vpiRange}, typespec_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - break; - } - case vpiByteTypespec: { - packed_ranges.push_back(make_range(7, 0)); - break; - } - case vpiEnumTypespec: - case vpiRealTypespec: - case vpiStringTypespec: { - break; - } - case vpiIntTypespec: - case vpiIntegerTypespec: { - visit_one_to_many({vpiRange}, typespec_h, [&](AST::AstNode *node) { packed_ranges.push_back(node); }); - if (packed_ranges.empty()) { - packed_ranges.push_back(make_range(31, 0)); - } - break; - } - case vpiShortIntTypespec: { - packed_ranges.push_back(make_range(15, 0)); - break; - } - case vpiTimeTypespec: - case vpiLongIntTypespec: { - packed_ranges.push_back(make_range(63, 0)); - break; - } - case vpiUnionTypespec: - case vpiStructTypespec: { - visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) { - if (node && !node->str.empty()) { - auto wiretype_node = make_ast_node(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - } - current_node->is_custom_type = true; - auto it = shared.param_types.find(current_node->str); - if (it == shared.param_types.end()) { - shared.param_types.insert(std::make_pair(current_node->str, node)); - } else { - delete node; - } - }); - break; - } - case vpiPackedArrayTypespec: - case vpiArrayTypespec: { - visit_one_to_one({vpiElemTypespec}, typespec_h, [&](AST::AstNode *node) { - if (!node->str.empty()) { - auto wiretype_node = make_ast_node(AST::AST_WIRETYPE); - wiretype_node->str = node->str; - current_node->children.push_back(wiretype_node); - current_node->is_custom_type = true; - auto it = shared.param_types.find(current_node->str); - if (it == shared.param_types.end()) - shared.param_types.insert(std::make_pair(current_node->str, node->clone())); - } - if (node && node->attributes.count(UhdmAst::packed_ranges())) { - for (auto r : node->attributes[UhdmAst::packed_ranges()]->children) { - packed_ranges.push_back(r->clone()); - } - } - delete node; - }); - 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_parameter: '%.*s' of type '%s'\n", (int)object->VpiFile().length(), - object->VpiFile().data(), object->VpiLineNo(), (int)object->VpiName().length(), object->VpiName().data(), - UHDM::VpiTypeName(typespec_h).c_str()); - break; - } - } - vpi_release_handle(typespec_h); - } - AST::AstNode *constant_node = process_value(obj_h); - if (constant_node) { - constant_node->filename = current_node->filename; - constant_node->location = current_node->location; - current_node->children.push_back(constant_node); - } - add_multirange_wire(current_node, packed_ranges, unpacked_ranges); -} - -void UhdmAst::process_byte_var() -{ - current_node = make_ast_node(AST::AST_WIRE); - current_node->children.push_back(make_range(7, 0)); - current_node->is_signed = vpi_get(vpiSigned, obj_h); -} - -void UhdmAst::process_long_int_var() -{ - current_node = make_ast_node(AST::AST_WIRE); - current_node->children.push_back(make_range(63, 0)); - current_node->is_signed = vpi_get(vpiSigned, obj_h); -} - -void UhdmAst::process_immediate_cover() -{ - current_node = make_ast_node(AST::AST_COVER); - visit_one_to_one({vpiExpr}, obj_h, [&](AST::AstNode *node) { - if (node) { - current_node->children.push_back(node); - } - }); -} - -void UhdmAst::process_immediate_assume() -{ - current_node = make_ast_node(AST::AST_ASSUME); - visit_one_to_one({vpiExpr}, obj_h, [&](AST::AstNode *node) { - if (node) { - current_node->children.push_back(node); - } - }); -} - -void UhdmAst::process_while() -{ - auto loop_id = shared.next_loop_id(); - current_node = make_ast_node(AST::AST_BLOCK); - current_node->str = "$whiledecl_block" + std::to_string(loop_id); - auto *loop = make_ast_node(AST::AST_WHILE); - loop->str = "$loop" + std::to_string(loop_id); - current_node->children.push_back(loop); - visit_one_to_one({vpiCondition}, obj_h, [&](AST::AstNode *node) { loop->children.push_back(node); }); - visit_one_to_one({vpiStmt}, obj_h, [&](AST::AstNode *node) { - if (node->type != AST::AST_BLOCK) { - node = make_ast_node(AST::AST_BLOCK, {node}); - } - if (node->str.empty()) { - node->str = loop->str; // Needed in simplify step - } - loop->children.push_back(node); - }); - transform_breaks_continues(loop, current_node); -} - -void UhdmAst::process_gate() -{ - current_node = make_ast_node(AST::AST_PRIMITIVE); - switch (vpi_get(vpiPrimType, obj_h)) { - case vpiAndPrim: - current_node->str = "and"; - break; - case vpiNandPrim: - current_node->str = "nand"; - break; - case vpiNorPrim: - current_node->str = "nor"; - break; - case vpiOrPrim: - current_node->str = "or"; - break; - case vpiXorPrim: - current_node->str = "xor"; - break; - case vpiXnorPrim: - current_node->str = "xnor"; - break; - case vpiBufPrim: - current_node->str = "buf"; - break; - case vpiNotPrim: - current_node->str = "not"; - break; - default: - log_file_error(current_node->filename, current_node->location.first_line, "Encountered unhandled gate type: %s", current_node->str.c_str()); - break; - } - visit_one_to_many({vpiPrimTerm}, obj_h, [&](AST::AstNode *node) { current_node->children.push_back(node); }); -} - -void UhdmAst::process_primterm() -{ - current_node = make_ast_node(AST::AST_ARGUMENT); - visit_one_to_one({vpiExpr}, obj_h, [&](AST::AstNode *node) { current_node->children.push_back(node); }); -} - -void UhdmAst::process_unsupported_stmt(const UHDM::BaseClass *object, bool is_error) -{ - const auto log_func = is_error ? log_error : log_warning; - std::string prefix = object->VpiLineNo() ? (std::string(object->VpiFile()) + ":" + std::to_string(object->VpiLineNo()) + ": ") : ""; - log_func("%sCurrently not supported object of type '%s'\n", prefix.c_str(), UHDM::VpiTypeName(obj_h).c_str()); -} - -void UhdmAst::process_type_parameter() -{ - current_node = make_ast_node(AST::AST_PARAMETER); - - // Use an attribute to distinguish "type parameters" from other parameters - set_attribute(current_node, attr_id::is_type_parameter, AST::AstNode::mkconst_int(1, false, 1)); - std::string renamed_enum; - - visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) { - if (!node) - return; - - if (node->type == AST::AST_WIRE && node->str.empty()) { - // anonymous type - get_attribute(current_node, attr_id::is_type_parameter)->str = "anonymous_parameter" + std::to_string(shared.next_anonymous_type_id()); - delete node; - return; - } - - if (node->type == AST::AST_ENUM) { - // Enum typedefs are composed of AST_ENUM and AST_TYPEDEF where the enum shall be renamed, - // so that the original name used in code is assigned to the AST_TYPEDEF node, - // and a mangled name is assigned to the AST_ENUM node. - renamed_enum = node->str + "$enum" + std::to_string(shared.next_enum_id()); - } - - current_node->children.push_back(node->clone()); - - // The child stores information about the type assigned to the parameter - // this information will be used to rename the module - - // find the typedef for `node` in the upper scope and copy it to .children of the AST_PARAMETER node - // if unable to find the typedef, continue without error as this could be a globally available type - - if (shared.current_top_node) { - for (auto child : shared.current_top_node->children) { - // name of the type we're looking for - if (child->str == node->str && child->type == AST::AST_TYPEDEF) { - current_node->children.push_back(child->clone()); - break; - } - } - } - delete node; - }); - - if (!renamed_enum.empty()) { - for (auto child : current_node->children) { - if (child->type == AST::AST_TYPEDEF) { - log_assert(child->children.size() > 0); - set_attribute(child->children[0], ID::enum_type, AST::AstNode::mkconst_str(renamed_enum)); - } - if (child->type == AST::AST_ENUM) { - child->str = renamed_enum; - // Names of enum variants need to be unique even accross Enums, otherwise Yosys fails. - for (auto grandchild : child->children) { - grandchild->str = renamed_enum + "." + grandchild->str; - } - } - } - } -} - -AST::AstNode *UhdmAst::process_object(vpiHandle obj_handle) -{ - obj_h = obj_handle; - const unsigned object_type = vpi_get(vpiType, obj_h); - const uhdm_handle *const handle = (const uhdm_handle *)obj_h; - const UHDM::BaseClass *const object = (const UHDM::BaseClass *)handle->object; - for (auto *obj : shared.nonSynthesizableObjects) { - UHDM::CompareContext ctx; - if (!object->Compare(obj, &ctx)) { - log_warning("%.*s:%d: Skipping non-synthesizable object of type '%s'\n", (int)object->VpiFile().length(), object->VpiFile().data(), - object->VpiLineNo(), UHDM::VpiTypeName(obj_h).c_str()); - return nullptr; - } - } - - if (shared.debug_flag) { - std::cout << indent << "Object '" << object->VpiName() << "' of type '" << UHDM::VpiTypeName(obj_h) << '\'' << std::endl; - } - - switch (object_type) { - case vpiDesign: - process_design(); - break; - case vpiParameter: - process_parameter(); - break; - case vpiPort: - process_port(); - break; - case vpiModule: - process_module(); - break; - case vpiStructTypespec: - process_struct_typespec(); - break; - case vpiUnionTypespec: - process_union_typespec(); - break; - case vpiPackedArrayTypespec: - process_packed_array_typespec(); - break; - case vpiArrayTypespec: - process_array_typespec(); - break; - case vpiTypespecMember: - process_typespec_member(); - break; - case vpiEnumTypespec: - process_enum_typespec(); - break; - case vpiEnumConst: - process_enum_const(); - break; - case vpiEnumVar: - case vpiEnumNet: - case vpiStructVar: - case vpiStructNet: - case vpiUnionVar: - process_custom_var(); - break; - case vpiShortIntVar: - case vpiIntVar: - case vpiIntegerVar: - process_int_var(); - break; - case vpiShortRealVar: - case vpiRealVar: - process_real_var(); - break; - case vpiPackedArrayVar: - process_packed_array_var(); - break; - case vpiArrayVar: - process_array_var(); - break; - case vpiParamAssign: - process_param_assign(); - break; - case vpiContAssign: - process_cont_assign(); - break; - case vpiAssignStmt: - case vpiAssignment: - process_assignment(object); - break; - case vpiInterfaceTypespec: - case vpiRefVar: - case vpiRefObj: - current_node = make_ast_node(AST::AST_IDENTIFIER); - break; - case vpiNet: - process_net(); - break; - case vpiArrayNet: - process_array_net(object); - break; - case vpiPackedArrayNet: - process_packed_array_net(); - break; - case vpiPackage: - process_package(); - break; - case vpiInterface: - process_interface(); - break; - case vpiModport: - process_modport(); - break; - case vpiIODecl: - process_io_decl(); - break; - case vpiAlways: - process_always(); - break; - case vpiEventControl: - process_event_control(object); - break; - case vpiInitial: - process_initial(); - break; - case vpiFinal: - process_unsupported_stmt(object, false); - break; - case vpiNamedBegin: - process_begin(true); - break; - case vpiBegin: - process_begin(false); - break; - case vpiCondition: - case vpiOperation: - process_operation(object); - break; - case vpiTaggedPattern: - process_tagged_pattern(); - break; - case vpiBitSelect: - process_bit_select(); - break; - case vpiPartSelect: - process_part_select(); - break; - case vpiIndexedPartSelect: - process_indexed_part_select(); - break; - case vpiVarSelect: - process_var_select(); - break; - case vpiIf: - case vpiIfElse: - process_if_else(); - break; - case vpiFor: - process_for(); - break; - case vpiBreak: - // Will be resolved later by loop processor - current_node = make_ast_node(static_cast(AST::Extended::AST_BREAK)); - break; - case vpiContinue: - // Will be resolved later by loop processor - current_node = make_ast_node(static_cast(AST::Extended::AST_CONTINUE)); - break; - case vpiGenScopeArray: - process_gen_scope_array(); - break; - case vpiGenScope: - process_gen_scope(); - break; - case vpiCase: - process_case(); - break; - case vpiCaseItem: - process_case_item(); - break; - case vpiConstant: - current_node = process_value(obj_h); - break; - case vpiRange: - process_range(object); - break; - case vpiReturn: - process_return(); - break; - case vpiFunction: - case vpiTask: - process_function(); - break; - case vpiBitVar: - case vpiLogicVar: - process_logic_var(); - break; - case vpiSysFuncCall: - process_sys_func_call(); - break; - case vpiFuncCall: - process_tf_call(AST::AST_FCALL); - break; - case vpiTaskCall: - process_tf_call(AST::AST_TCALL); - break; - case vpiImmediateAssert: - if (!shared.no_assert) - process_immediate_assert(); - break; - case vpiAssert: - if (!shared.no_assert) - process_unsupported_stmt(object); - break; - case vpiHierPath: - process_hier_path(); - break; - case UHDM::uhdmimport_typespec: - break; - case vpiLogicTypespec: - process_logic_typespec(); - break; - case vpiIntTypespec: - case vpiIntegerTypespec: - process_int_typespec(); - break; - case vpiShortIntTypespec: - process_shortint_typespec(); - break; - case vpiLongIntTypespec: - process_longint_typespec(); - break; - case vpiTimeTypespec: - process_time_typespec(); - break; - case vpiBitTypespec: - process_bit_typespec(); - break; - case vpiByteTypespec: - process_byte_typespec(); - break; - case vpiStringVar: - process_string_var(); - break; - case vpiStringTypespec: - process_string_typespec(); - break; - case vpiRepeat: - process_repeat(); - break; - case vpiByteVar: - process_byte_var(); - break; - case vpiLongIntVar: - process_long_int_var(); - break; - case vpiImmediateCover: - process_immediate_cover(); - break; - case vpiImmediateAssume: - process_immediate_assume(); - break; - case vpiAssume: - process_unsupported_stmt(object); - break; - case vpiWhile: - process_while(); - break; - case vpiGate: - process_gate(); - break; - case vpiPrimTerm: - process_primterm(); - break; - case vpiClockingBlock: - process_unsupported_stmt(object); - break; - case vpiTypeParameter: - process_type_parameter(); - break; - case vpiProgram: - default: - report_error("%.*s:%d: Encountered unhandled object '%.*s' of type '%s'\n", (int)object->VpiFile().length(), object->VpiFile().data(), - object->VpiLineNo(), (int)object->VpiName().length(), object->VpiName().data(), UHDM::VpiTypeName(obj_h).c_str()); - break; - } - - // Check if we initialized the node in switch-case - if (current_node) { - if (current_node->type != AST::AST_NONE) { - return current_node; - } - } - return nullptr; -} - -AST::AstNode *UhdmAst::visit_designs(const std::vector &designs) -{ - attr_id_init(); - - current_node = new AST::AstNode(AST::AST_DESIGN); - for (auto design : designs) { - UhdmAst ast(this, shared, indent); - auto *processed_design_node = ast.process_object(design); - // Flatten multiple designs into one - current_node->children = std::move(processed_design_node->children); - delete processed_design_node; - } - - for (auto &[name, node] : shared.param_types) { - delete node; - } - shared.param_types.clear(); - - // Remove all internal attributes from the AST. - visitEachDescendant(current_node, delete_internal_attributes); - - attr_id_cleanup(); - - return current_node; -} - -void UhdmAst::report_error(const char *format, ...) const -{ - va_list args; - va_start(args, format); - if (shared.stop_on_error) { - logv_error(format, args); - } else { - logv_warning(format, args); - } -} - -} // namespace systemverilog_plugin diff --git a/systemverilog-plugin/UhdmAst.h b/systemverilog-plugin/UhdmAst.h deleted file mode 100644 index d60e82700..000000000 --- a/systemverilog-plugin/UhdmAst.h +++ /dev/null @@ -1,327 +0,0 @@ -#ifndef _UHDM_AST_H_ -#define _UHDM_AST_H_ 1 - -#include "frontends/ast/ast.h" -#include -#undef cover - -#include "uhdmastshared.h" -#include -#include - -namespace systemverilog_plugin -{ - -class AstNodeBuilder; - -class UhdmAst -{ - private: - // Logging method for exclusive use of `uhdmast_assert` macro. - void uhdmast_assert_log(const char *expr_str, const char *func, const char *file, int line) const; - - // 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 child_node_types, vpiHandle parent_handle, const std::function &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 child_node_types, vpiHandle parent_handle, const std::function &f); - - // Visit children of type vpiRange that belong to the given parent node. - void visit_range(vpiHandle obj_h, const std::function &f); - - // Visit the default expression assigned to a variable. - void visit_default_expr(vpiHandle obj_h); - - // Reads location info (start/end line/column numbers, file name) from `obj_h` and sets them on `target_node`. - void apply_location_from_current_obj(::Yosys::AST::AstNode &target_node) const; - // Reads object name from `obj_h` and assigns it to `target_node`. - void apply_name_from_current_obj(::Yosys::AST::AstNode &target_node) const; - - // Creates node of specified `type` with location properties read from `obj_h`. - AstNodeBuilder make_node(::Yosys::AST::AstNodeType type) const; - // Creates node of specified `type` with location properties and name read from `obj_h`. - AstNodeBuilder make_named_node(::Yosys::AST::AstNodeType type) const; - // Creates AST_IDENTIFIER node with specified `id` and location properties read from `obj_h`. - AstNodeBuilder make_ident(std::string id) const; - // Creates signed AST_CONSTANT node with specified `value` and location properties read from `obj_h`. - AstNodeBuilder make_const(int32_t value, uint8_t width = 32) const; - // Creates unsigned AST_CONSTANT node with specified `value` and location properties read from `obj_h`. - AstNodeBuilder make_const(uint32_t value, uint8_t width = 32) const; - - // Create an AstNode of the specified type with metadata extracted from - // the given vpiHandle. - // OBSOLETE: use `make_node` or `make_named_node` instead. - ::Yosys::AST::AstNode *make_ast_node(::Yosys::AST::AstNodeType type, std::vector<::Yosys::AST::AstNode *> children = {}); - - // Create an identifier AstNode - // OBSOLETE: use `make_ident` instead. - ::Yosys::AST::AstNode *make_identifier(std::string name); - - // Makes the passed node a cell node of the specified 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(::Yosys::AST::AstNode *current_node, ::Yosys::AST::AstNode *type_node); - - // Go up the UhdmAst to find a parent node of the specified type - ::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 - ::Yosys::AST::AstNode *process_value(vpiHandle obj_h); - - // Transforms break and continue nodes into structures accepted by the AST frontend - void transform_breaks_continues(::Yosys::AST::AstNode *loop, ::Yosys::AST::AstNode *decl_block); - - // The parent UhdmAst - UhdmAst *parent; - - // Data shared between all UhdmAst objects - UhdmAstShared &shared; - - // The current VPI/UHDM handle - vpiHandle obj_h = 0; - - // The current Yosys AST node - ::Yosys::AST::AstNode *current_node = nullptr; - - // Indentation used for debug printing - std::string indent; - - // Mapping of names that should be replaced to new names - std::unordered_map node_renames; - - // Functions that process specific types of nodes - void process_design(); - void process_parameter(); - void process_port(); - void process_module(); - void process_struct_typespec(); - void process_union_typespec(); - void process_packed_array_typespec(); - void process_array_typespec(); - void process_typespec_member(); - void process_enum_typespec(); - void process_enum_const(); - void process_custom_var(); - void process_int_var(); - void process_real_var(); - void process_array_var(); - void process_packed_array_var(); - void process_param_assign(); - void process_cont_assign(); - void process_cont_assign_net(); - void process_cont_assign_var_init(); - void process_assignment(const UHDM::BaseClass *object); - void process_net(); - void process_packed_array_net(); - void process_array_net(const UHDM::BaseClass *object); - void process_package(); - void process_interface(); - void process_modport(); - void process_io_decl(); - void process_always(); - void process_event_control(const UHDM::BaseClass *object); - void process_initial(); - void process_begin(bool is_named); - void process_operation(const UHDM::BaseClass *object); - void process_stream_op(); - void process_list_op(); - void process_cast_op(); - void process_inside_op(); - void process_assignment_pattern_op(); - void process_tagged_pattern(); - void process_bit_select(); - void process_part_select(); - void process_indexed_part_select(); - void process_var_select(); - void process_if_else(); - void process_for(); - void process_gen_scope_array(); - void process_gen_scope(); - void process_case(); - void process_case_item(); - void process_range(const UHDM::BaseClass *object); - void process_return(); - void process_function(); - void process_logic_var(); - void process_sys_func_call(); - // use for task calls and function calls - void process_tf_call(::Yosys::AST::AstNodeType type); - void process_immediate_assert(); - void process_hier_path(); - void process_logic_typespec(); - void process_int_typespec(); - void process_shortint_typespec(); - void process_longint_typespec(); - void process_time_typespec(); - void process_bit_typespec(); - void process_string_var(); - void process_string_typespec(); - void process_repeat(); - void process_byte_var(); - void process_byte_typespec(); - void process_long_int_var(); - void process_immediate_cover(); - void process_immediate_assume(); - void process_while(); - void process_gate(); - void process_primterm(); - void process_type_parameter(); - void simplify_parameter(::Yosys::AST::AstNode *parameter, ::Yosys::AST::AstNode *module_node = nullptr); - void process_unsupported_stmt(const UHDM::BaseClass *object, bool is_error = true); - - UhdmAst(UhdmAst *p, UhdmAstShared &s, const std::string &i) : parent(p), shared(s), indent(i) - { - if (parent) - node_renames = parent->node_renames; - } - - public: - UhdmAst(UhdmAstShared &s, const std::string &i = "") : UhdmAst(nullptr, s, i) {} - - // Visits single VPI object and creates proper AST node - ::Yosys::AST::AstNode *process_object(vpiHandle obj_h); - - // Visits all VPI design objects and returns created ASTs - ::Yosys::AST::AstNode *visit_designs(const std::vector &designs); - - 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 ::Yosys::IdString &force_convert(); - static const ::Yosys::IdString &is_imported(); - static const ::Yosys::IdString &is_simplified_wire(); - static const ::Yosys::IdString &low_high_bound(); - static const ::Yosys::IdString &is_elaborated_module(); -}; - -// Utility for building AstNode trees. -// -// The object members that set AstNode properties return rvalue reference to *this (i.e. to the builder object), so they can be chained. -// The children list is set using call operator (`builder_object({child0, child1, ...})`). -// Build finalization is done through cast operator to either `AstNode*` or `std::unique_ptr`. -// -// Usage example: -// -// 1. Define one or more factory functions for creating base AstNode object: -// -// const auto make_node = [](AST::AstNodeType type) { -// auto node = std::make_unique(type); -// // ...initialize the node if needed... -// return AstNodeBuilder(std::move(node)); -// }; -// -// 2. Use the factories to create a tree: -// -// // AST::AstNode *const variable_node = ... -// // AST::AstNode *const value_node = ... -// AST::AstNode *const assign = // -// (make_node(AST::AST_ASSIGN_EQ))({ -// (make_node(AST::AST_IDENTIFIER).str(variable_node->str)), -// (make_node(Yosys::AST::AST_ADD))({ -// (make_node(AST::AST_IDENTIFIER).str(value_node->str)), -// (make_node(AST::AST_CONSTANT).value(4)), -// }), -// }); -// -// In the real code instead of custom factories illustrated in point 1 above, you probably should use predefined methods from `UhdmAst` class. -// The syntax above puts the factory call and all its method calls (but not the function call operator with the children list) in `()`. This is done -// to make `clang-format` format the code as presented. Otherwise it is heavily wrapped and a lot less readable. `()` are technically not required -// in leafs to make them format as expected, but its nice to use them for consistency. -class AstNodeBuilder -{ - using AstNode = ::Yosys::AST::AstNode; - using AstNodeType = ::Yosys::AST::AstNodeType; - - std::unique_ptr node; - - public: - explicit AstNodeBuilder(AstNodeType node_type) : node(new AstNode(node_type)) {} - explicit AstNodeBuilder(std::unique_ptr node) : node(std::move(node)) {} - ~AstNodeBuilder() { log_assert(node == nullptr); } - - AstNodeBuilder(AstNodeBuilder &&) = default; - - AstNodeBuilder() = delete; - AstNodeBuilder(const AstNodeBuilder &) = delete; - AstNodeBuilder &operator=(const AstNodeBuilder &) = delete; - AstNodeBuilder &operator=(AstNodeBuilder &&) = delete; - - // Property setters - - // Sets `AstNode::children` vector - AstNodeBuilder &&operator()(std::vector children) { return node->children = std::move(children), std::move(*this); } - - // Sets `AstNode::str` value. - AstNodeBuilder &&str(std::string s) { return node->str = std::move(s), std::move(*this); } - - // Sets `AstNode::integer` value. - AstNodeBuilder &&integer(uint32_t v) { return node->integer = v, std::move(*this); } - - // Sets `AstNode::is_signed` value. - AstNodeBuilder &&is_signed(bool v) { return node->is_signed = v, std::move(*this); } - - // Sets `AstNode::is_reg` value. - AstNodeBuilder &&is_reg(bool v) { return node->is_reg = v, std::move(*this); } - - // Sets `AstNode::range_valid`. - AstNodeBuilder &&range_valid(bool v) { return node->range_valid = v, std::move(*this); } - - // Convenience range setters - - // Sets `AstNode::range_left`, `AstNode::range_right`, `AstNode::range_valid`. - AstNodeBuilder &&range(bool v, int left = -1, int right = 0) - { - node->range_valid = v; - node->range_left = left; - node->range_right = right; - return std::move(*this); - } - - // Sets `AstNode::range_left`, `AstNode::range_right`, `AstNode::range_valid = true`. - AstNodeBuilder &&range(int left, int right) { return range(true, left, right); } - - // Convenience value setters, mainly for constants. - - // Sets node's value. - // Sets: `AstNode::integer`, `AstNode::is_signed`, `AstNode::bits`. - AstNodeBuilder &&value(uint32_t v, bool is_signed, int width = 32) - { - log_assert(width >= 0); - node->integer = v; - node->is_signed = is_signed; - // `AstNode::mkconst_int` does this too. - for (int i = 0; i < width; i++) { - node->bits.push_back((v & 1) ? Yosys::RTLIL::State::S1 : Yosys::RTLIL::State::S0); - v = v >> 1; - } - range(width - 1, 0); - return std::move(*this); - } - - // Sets node's value to signed 32 bit integer. - // Sets: `AstNode::integer`, `AstNode::is_signed`, `AstNode::bits`. - AstNodeBuilder &&value(int32_t v) { return value(v, true); } - - // Sets node's value to unsigned 32 bit integer. - // Sets: `AstNode::integer`, `AstNode::is_signed`, `AstNode::bits`. - AstNodeBuilder &&value(uint32_t v) { return value(v, false); } - - // Type-cast operators used for building. - - operator AstNode *() { return node.release(); } - - operator std::unique_ptr() { return std::move(node); } -}; - -} // namespace systemverilog_plugin - -#endif diff --git a/systemverilog-plugin/tests/Makefile b/systemverilog-plugin/tests/Makefile deleted file mode 100644 index 3caff1441..000000000 --- a/systemverilog-plugin/tests/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -# 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 - -TESTS = counter \ - break_continue \ - separate-compilation \ - debug-flag \ - defines \ - defaults \ - formal \ - translate_off - -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 -defaults_verify = true -defines_verify = true -formal_verify = true -translate_off_verify = true - -.PHONY: systemverilog_tests_clean -systemverilog_tests_clean: - @rm -rf $(foreach test,$(TESTS),$(test)/tmp) - -clean: systemverilog_tests_clean diff --git a/systemverilog-plugin/tests/break_continue/break_continue.golden.out b/systemverilog-plugin/tests/break_continue/break_continue.golden.out deleted file mode 100644 index 74a27c291..000000000 --- a/systemverilog-plugin/tests/break_continue/break_continue.golden.out +++ /dev/null @@ -1,2 +0,0 @@ -top a - - po 110 -top b - - po 15 diff --git a/systemverilog-plugin/tests/break_continue/break_continue.tcl b/systemverilog-plugin/tests/break_continue/break_continue.tcl deleted file mode 100644 index d49636746..000000000 --- a/systemverilog-plugin/tests/break_continue/break_continue.tcl +++ /dev/null @@ -1,11 +0,0 @@ -yosys -import -if { [info procs read_uhdm] == {} } { plugin -i systemverilog } -yosys -import ;# ingest plugin commands - -set TMP_DIR $::env(TEST_OUTPUT_PREFIX)/tmp -file mkdir $TMP_DIR - -# Testing simple round-trip -read_systemverilog -o $TMP_DIR $::env(DESIGN_TOP).v -prep -write_table [test_output_path $::env(DESIGN_TOP).out] diff --git a/systemverilog-plugin/tests/break_continue/break_continue.v b/systemverilog-plugin/tests/break_continue/break_continue.v deleted file mode 100644 index d06d60b5f..000000000 --- a/systemverilog-plugin/tests/break_continue/break_continue.v +++ /dev/null @@ -1,30 +0,0 @@ -// 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(output int a, output int b); - initial begin - a = 0; - b = 0; - repeat(15) begin - if(a > 100) begin - if (b > 10) - break; - b = b + 5; - continue; - end - a = a + 10; - end - end -endmodule diff --git a/systemverilog-plugin/tests/counter/counter.tcl b/systemverilog-plugin/tests/counter/counter.tcl deleted file mode 100644 index 28a444750..000000000 --- a/systemverilog-plugin/tests/counter/counter.tcl +++ /dev/null @@ -1,10 +0,0 @@ -yosys -import -if { [info procs read_uhdm] == {} } { plugin -i systemverilog } -yosys -import ;# ingest plugin commands - -set TMP_DIR $::env(TEST_OUTPUT_PREFIX)/tmp -file mkdir $TMP_DIR - -# Testing simple round-trip -read_systemverilog -o $TMP_DIR $::env(DESIGN_TOP).v -write_verilog diff --git a/systemverilog-plugin/tests/counter/counter.v b/systemverilog-plugin/tests/counter/counter.v deleted file mode 100644 index 3dabd7e7f..000000000 --- a/systemverilog-plugin/tests/counter/counter.v +++ /dev/null @@ -1,33 +0,0 @@ -// 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 -); - localparam BITS = 4; - localparam LOG2DELAY = 22; - - wire bufg; - BUFG bufgctrl ( - .I(clk), - .O(bufg) - ); - reg [BITS+LOG2DELAY-1:0] counter = 0; - always @(posedge bufg) begin - counter <= counter + 1; - end - assign led[3:0] = counter >> LOG2DELAY; -endmodule diff --git a/systemverilog-plugin/tests/debug-flag/debug-flag-buf.sv b/systemverilog-plugin/tests/debug-flag/debug-flag-buf.sv deleted file mode 100644 index 565946b5d..000000000 --- a/systemverilog-plugin/tests/debug-flag/debug-flag-buf.sv +++ /dev/null @@ -1,21 +0,0 @@ -// 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 deleted file mode 100644 index b0362fcf9..000000000 --- a/systemverilog-plugin/tests/debug-flag/debug-flag-pkg.sv +++ /dev/null @@ -1,19 +0,0 @@ -// 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 deleted file mode 100644 index 3b3a56867..000000000 --- a/systemverilog-plugin/tests/debug-flag/debug-flag.tcl +++ /dev/null @@ -1,14 +0,0 @@ -yosys -import -if { [info procs read_uhdm] == {} } { plugin -i systemverilog } -yosys -import ;# ingest plugin commands - -set TMP_DIR $::env(TEST_OUTPUT_PREFIX)/tmp -file mkdir $TMP_DIR - -# Testing simple round-trip -read_systemverilog -debug -odir $TMP_DIR -defer $::env(DESIGN_TOP)-pkg.sv -read_systemverilog -debug -odir $TMP_DIR -defer $::env(DESIGN_TOP)-buf.sv -read_systemverilog -debug -odir $TMP_DIR -defer $::env(DESIGN_TOP).v -read_systemverilog -debug -odir $TMP_DIR -link -hierarchy -write_verilog diff --git a/systemverilog-plugin/tests/debug-flag/debug-flag.v b/systemverilog-plugin/tests/debug-flag/debug-flag.v deleted file mode 100644 index 5bd294a08..000000000 --- a/systemverilog-plugin/tests/debug-flag/debug-flag.v +++ /dev/null @@ -1,31 +0,0 @@ -// 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 deleted file mode 100644 index 804d2e519..000000000 --- a/systemverilog-plugin/tests/defaults/defaults.tcl +++ /dev/null @@ -1,21 +0,0 @@ -yosys -import -if { [info procs read_uhdm] == {} } { plugin -i systemverilog } -yosys -import ;# ingest plugin commands - -set TMP_DIR $::env(TEST_OUTPUT_PREFIX)/tmp -file mkdir $TMP_DIR - -# Define forbidden value -systemverilog_defaults -add -DPAKALA -# Stash it -systemverilog_defaults -push -systemverilog_defaults -clear -read_systemverilog -o $TMP_DIR $::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 -o $TMP_DIR $::env(DESIGN_TOP).v -hierarchy -write_verilog diff --git a/systemverilog-plugin/tests/defaults/defaults.v b/systemverilog-plugin/tests/defaults/defaults.v deleted file mode 100644 index f565a77e2..000000000 --- a/systemverilog-plugin/tests/defaults/defaults.v +++ /dev/null @@ -1,28 +0,0 @@ -// 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 deleted file mode 100644 index 14b37adbe..000000000 --- a/systemverilog-plugin/tests/defines/defines.tcl +++ /dev/null @@ -1,13 +0,0 @@ -yosys -import -if { [info procs read_uhdm] == {} } { plugin -i systemverilog } -yosys -import ;# ingest plugin commands - -set TMP_DIR $::env(TEST_OUTPUT_PREFIX)/tmp -file mkdir $TMP_DIR - -systemverilog_defines -DPONA -systemverilog_defines -DPAKALA -systemverilog_defines -UPAKALA -read_systemverilog -o $TMP_DIR $::env(DESIGN_TOP).v -hierarchy -write_verilog diff --git a/systemverilog-plugin/tests/defines/defines.v b/systemverilog-plugin/tests/defines/defines.v deleted file mode 100644 index 528e9f19f..000000000 --- a/systemverilog-plugin/tests/defines/defines.v +++ /dev/null @@ -1,29 +0,0 @@ -// 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/formal/formal.tcl b/systemverilog-plugin/tests/formal/formal.tcl deleted file mode 100644 index b2590d3c9..000000000 --- a/systemverilog-plugin/tests/formal/formal.tcl +++ /dev/null @@ -1,10 +0,0 @@ -yosys -import -if { [info procs read_uhdm] == {} } { plugin -i systemverilog } -yosys -import ;# ingest plugin commands - -set TMP_DIR $::env(TEST_OUTPUT_PREFIX)/tmp -file mkdir $TMP_DIR - -read_systemverilog -o $TMP_DIR -formal $::env(DESIGN_TOP).v -hierarchy -write_verilog diff --git a/systemverilog-plugin/tests/formal/formal.v b/systemverilog-plugin/tests/formal/formal.v deleted file mode 100644 index 04e346cec..000000000 --- a/systemverilog-plugin/tests/formal/formal.v +++ /dev/null @@ -1,33 +0,0 @@ -// 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 -); - -`ifdef SYNTHESIS - initial $stop("SYNTHESIS should be undefined"); -`endif -`ifndef YOSYS - initial $stop("YOSYS should be defined"); -`endif -`ifndef FORMAL - initial $stop("FORMAL should be defined"); -`endif - assign out = clk; -endmodule diff --git a/systemverilog-plugin/tests/separate-compilation/separate-compilation-buf.sv b/systemverilog-plugin/tests/separate-compilation/separate-compilation-buf.sv deleted file mode 100644 index 565946b5d..000000000 --- a/systemverilog-plugin/tests/separate-compilation/separate-compilation-buf.sv +++ /dev/null @@ -1,21 +0,0 @@ -// 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/separate-compilation/separate-compilation-pkg.sv b/systemverilog-plugin/tests/separate-compilation/separate-compilation-pkg.sv deleted file mode 100644 index b0362fcf9..000000000 --- a/systemverilog-plugin/tests/separate-compilation/separate-compilation-pkg.sv +++ /dev/null @@ -1,19 +0,0 @@ -// 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/separate-compilation/separate-compilation.tcl b/systemverilog-plugin/tests/separate-compilation/separate-compilation.tcl deleted file mode 100644 index 46b029a4c..000000000 --- a/systemverilog-plugin/tests/separate-compilation/separate-compilation.tcl +++ /dev/null @@ -1,14 +0,0 @@ -yosys -import -if { [info procs read_uhdm] == {} } { plugin -i systemverilog } -yosys -import ;# ingest plugin commands - -set TMP_DIR $::env(TEST_OUTPUT_PREFIX)/tmp -file mkdir $TMP_DIR - -# Testing simple round-trip -read_systemverilog -odir $TMP_DIR -defer $::env(DESIGN_TOP)-pkg.sv -read_systemverilog -odir $TMP_DIR -defer $::env(DESIGN_TOP)-buf.sv -read_systemverilog -odir $TMP_DIR -defer $::env(DESIGN_TOP).v -read_systemverilog -odir $TMP_DIR -link -hierarchy -write_verilog diff --git a/systemverilog-plugin/tests/separate-compilation/separate-compilation.v b/systemverilog-plugin/tests/separate-compilation/separate-compilation.v deleted file mode 100644 index 5bd294a08..000000000 --- a/systemverilog-plugin/tests/separate-compilation/separate-compilation.v +++ /dev/null @@ -1,31 +0,0 @@ -// 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/translate_off/translate_off.tcl b/systemverilog-plugin/tests/translate_off/translate_off.tcl deleted file mode 100644 index d836d1e2b..000000000 --- a/systemverilog-plugin/tests/translate_off/translate_off.tcl +++ /dev/null @@ -1,8 +0,0 @@ -yosys -import -if { [info procs read_uhdm] == {} } { plugin -i systemverilog } -yosys -import ;# ingest plugin commands - -set TMP_DIR $::env(TEST_OUTPUT_PREFIX)/tmp -file mkdir $TMP_DIR - -read_systemverilog -o $TMP_DIR $::env(DESIGN_TOP).v diff --git a/systemverilog-plugin/tests/translate_off/translate_off.v b/systemverilog-plugin/tests/translate_off/translate_off.v deleted file mode 100644 index 3ca8840eb..000000000 --- a/systemverilog-plugin/tests/translate_off/translate_off.v +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020-2023 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 i, output o); - // synopsys translate_off - initial $stop("Code between translate_off...translate_on should be ignored."); - // synopsys translate_on - assign o = i; -endmodule diff --git a/systemverilog-plugin/third_party/yosys/README b/systemverilog-plugin/third_party/yosys/README deleted file mode 100644 index df69e690b..000000000 --- a/systemverilog-plugin/third_party/yosys/README +++ /dev/null @@ -1,31 +0,0 @@ -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. -- simplify.cc: yosys/frontends/ast/simplify.cc (rev. ceef00c) - - The file is a part of Yosys AST frontend. It has been placed in the plugin, - as in some cases we need to adjust it to support certain functionalities - in the plugin. Since it is included now in the plugin, we can skip caling - the original Yosys' simplify() during AST preparation. The original Yosys' - simplify() is only called in uhdmcommonfrontend.cc when Yosys' process() - is called, after having AST done. - - Changes: - - Removed unneeded code and member functions of AstNode:: - - Modified usage of AstNode:: members that are called from the Yosys' - AstNode:: struct. - - The file will be extended in the future instead of simplify_sv() - in UhdmAst.cc, and it will be moved to other directory then. - -Non-copied files placed here for interfacing purposes: - -- const2ast.h -- simplify.h diff --git a/systemverilog-plugin/third_party/yosys/const2ast.cc b/systemverilog-plugin/third_party/yosys/const2ast.cc deleted file mode 100644 index 96ea6cc5b..000000000 --- a/systemverilog-plugin/third_party/yosys/const2ast.cc +++ /dev/null @@ -1,252 +0,0 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2012 Claire Xenia Wolf - * - * 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 -#include -#include - -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 &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 &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 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 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 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 "'[sS]?[bodhBODH]" syntax - if (*endptr == '\'') - { - std::vector 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 deleted file mode 100644 index f7130a2eb..000000000 --- a/systemverilog-plugin/third_party/yosys/const2ast.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SYSTEMVERILOG_PLUGIN_CONST2AST_H -#define SYSTEMVERILOG_PLUGIN_CONST2AST_H - -#include "frontends/ast/ast.h" -#include - -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/third_party/yosys/simplify.cc b/systemverilog-plugin/third_party/yosys/simplify.cc deleted file mode 100644 index cdc39b421..000000000 --- a/systemverilog-plugin/third_party/yosys/simplify.cc +++ /dev/null @@ -1,4086 +0,0 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2012 Claire Xenia Wolf - * - * 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. - * - * --- - * - * This is the AST frontend library. - * - * The AST frontend library is not a frontend on it's own but provides a - * generic abstract syntax tree (AST) abstraction for HDL code and can be - * used by HDL frontends. See "ast.h" for an overview of the API and the - * Verilog frontend for an usage example. - * - */ - -#include "kernel/log.h" -#include "libs/sha1/sha1.h" - -#include "const2ast.h" - -#include -#include -#include -#include - -#include "simplify.h" - -YOSYS_NAMESPACE_BEGIN -namespace VERILOG_FRONTEND -{ -extern bool sv_mode; -} -YOSYS_NAMESPACE_END - -namespace systemverilog_plugin -{ - -using namespace ::Yosys; -using namespace ::Yosys::AST_INTERNAL; - -void annotateTypedEnums(Yosys::AST::AstNode *ast_node, Yosys::AST::AstNode *template_node) -{ - //check if enum - if (template_node->attributes.count(ID::enum_type)) { - //get reference to enum node: - std::string enum_type = template_node->attributes[ID::enum_type]->str.c_str(); - // log("enum_type=%s (count=%lu)\n", enum_type.c_str(), current_scope.count(enum_type)); - // log("current scope:\n"); - // for (auto &it : current_scope) - // log(" %s\n", it.first.c_str()); - log_assert(current_scope.count(enum_type) == 1); - Yosys::AST::AstNode *enum_node = current_scope.at(enum_type); - log_assert(enum_node->type == Yosys::AST::AST_ENUM); - while (simplify(enum_node, true, false, false, 1, -1, false, true)) { } - //get width from 1st enum item: - log_assert(enum_node->children.size() >= 1); - Yosys::AST::AstNode *enum_item0 = enum_node->children[0]; - log_assert(enum_item0->type == Yosys::AST::AST_ENUM_ITEM); - int width; - if (!enum_item0->range_valid) - width = 1; - else if (enum_item0->range_swapped) - width = enum_item0->range_right - enum_item0->range_left + 1; - else - width = enum_item0->range_left - enum_item0->range_right + 1; - log_assert(width > 0); - //add declared enum items: - for (auto enum_item : enum_node->children){ - log_assert(enum_item->type == Yosys::AST::AST_ENUM_ITEM); - //get is_signed - bool is_signed; - if (enum_item->children.size() == 1){ - is_signed = false; - } else if (enum_item->children.size() == 2){ - log_assert(enum_item->children[1]->type == Yosys::AST::AST_RANGE); - is_signed = enum_item->children[1]->is_signed; - } else { - log_error("enum_item children size==%lu, expected 1 or 2 for %s (%s)\n", - enum_item->children.size(), - enum_item->str.c_str(), enum_node->str.c_str() - ); - } - //start building attribute string - std::string enum_item_str = "\\enum_value_"; - //get enum item value - if(enum_item->children[0]->type != Yosys::AST::AST_CONSTANT){ - log_error("expected const, got %s for %s (%s)\n", - type2str(enum_item->children[0]->type).c_str(), - enum_item->str.c_str(), enum_node->str.c_str() - ); - } - RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed); - enum_item_str.append(val.as_string()); - //set attribute for available val to enum item name mappings - ast_node->attributes[enum_item_str.c_str()] = Yosys::AST::AstNode::mkconst_str(enum_item->str); - } - } -} - -static bool name_has_dot(const std::string &name, std::string &struct_name) -{ - // check if plausible struct member name \sss.mmm - std::string::size_type pos; - if (name.substr(0, 1) == "\\" && (pos = name.find('.', 0)) != std::string::npos) { - struct_name = name.substr(0, pos); - return true; - } - return false; -} - -static Yosys::AST::AstNode *make_range(int left, int right, bool is_signed = false) -{ - // generate a pre-validated range node for a fixed signal range. - auto range = new Yosys::AST::AstNode(Yosys::AST::AST_RANGE); - range->range_left = left; - range->range_right = right; - range->range_valid = true; - range->children.push_back(Yosys::AST::AstNode::mkconst_int(left, true)); - range->children.push_back(Yosys::AST::AstNode::mkconst_int(right, true)); - range->is_signed = is_signed; - return range; -} - -static int range_width(Yosys::AST::AstNode *node, Yosys::AST::AstNode *rnode) -{ - log_assert(rnode->type==Yosys::AST::AST_RANGE); - if (!rnode->range_valid) { - log_file_error(node->filename, node->location.first_line, "Size must be constant in packed struct/union member %s\n", node->str.c_str()); - - } - // note: range swapping has already been checked for - return rnode->range_left - rnode->range_right + 1; -} - -[[noreturn]] static void struct_array_packing_error(Yosys::AST::AstNode *node) -{ - log_file_error(node->filename, node->location.first_line, "Unpacked array in packed struct/union member %s\n", node->str.c_str()); -} - -static void save_struct_range_dimensions(Yosys::AST::AstNode *node, Yosys::AST::AstNode *rnode) -{ - node->multirange_dimensions.push_back(rnode->range_right); - node->multirange_dimensions.push_back(range_width(node, rnode)); - node->multirange_swapped.push_back(rnode->range_swapped); -} - -static int get_struct_range_offset(Yosys::AST::AstNode *node, int dimension) -{ - return node->multirange_dimensions[2*dimension]; -} - -static int get_struct_range_width(Yosys::AST::AstNode *node, int dimension) -{ - return node->multirange_dimensions[2*dimension + 1]; -} - -static int size_packed_struct(Yosys::AST::AstNode *snode, int base_offset) -{ - // Struct members will be laid out in the structure contiguously from left to right. - // Union members all have zero offset from the start of the union. - // Determine total packed size and assign offsets. Store these in the member node. - bool is_union = (snode->type == Yosys::AST::AST_UNION); - int offset = 0; - int packed_width = -1; - - // embedded struct or union with range? - auto it = std::remove_if(snode->children.begin(), snode->children.end(), [](Yosys::AST::AstNode *node) { return node->type == Yosys::AST::AST_RANGE; }); - std::vector ranges(it, snode->children.end()); - snode->children.erase(it, snode->children.end()); - if (!ranges.empty()) { - if (ranges.size() > 1) { - log_file_error(ranges[1]->filename, ranges[1]->location.first_line, - "Currently support for custom-type with range is limited to single range\n"); - } - for (auto range : ranges) { - snode->multirange_dimensions.push_back(min(range->range_left, range->range_right)); - snode->multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1); - snode->multirange_swapped.push_back(range->range_swapped); - } - } - // examine members from last to first - for (auto it = snode->children.rbegin(); it != snode->children.rend(); ++it) { - auto node = *it; - int width; - if (node->type == Yosys::AST::AST_STRUCT || node->type == Yosys::AST::AST_UNION) { - // embedded struct or union - width = size_packed_struct(node, base_offset + offset); - // set range of struct - node->range_right = base_offset + offset; - node->range_left = base_offset + offset + width - 1; - node->range_valid = true; - } - else { - log_assert(node->type == Yosys::AST::AST_STRUCT_ITEM); - if (node->children.size() > 0 && node->children[0]->type == Yosys::AST::AST_RANGE) { - // member width e.g. bit [7:0] a - width = range_width(node, node->children[0]); - if (node->children.size() == 2) { - // Unpacked array. Note that this is a Yosys extension; only packed data types - // and integer data types are allowed in packed structs / unions in SystemVerilog. - if (node->children[1]->type == Yosys::AST::AST_RANGE) { - // Unpacked array, e.g. bit [63:0] a [0:3] - auto rnode = node->children[1]; - if (rnode->children.size() == 1) { - // C-style array size, e.g. bit [63:0] a [4] - node->multirange_dimensions.push_back(0); - node->multirange_dimensions.push_back(rnode->range_left); - node->multirange_swapped.push_back(true); - width *= rnode->range_left; - } else { - save_struct_range_dimensions(node, rnode); - width *= range_width(node, rnode); - } - save_struct_range_dimensions(node, node->children[0]); - } - else { - // The Yosys extension for unpacked arrays in packed structs / unions - // only supports memories, i.e. e.g. logic [7:0] a [256] - see above. - struct_array_packing_error(node); - } - } else { - // Vector - save_struct_range_dimensions(node, node->children[0]); - } - // range nodes are now redundant - for (Yosys::AST::AstNode *child : node->children) - delete child; - node->children.clear(); - } - else if (node->children.size() > 0 && node->children[0]->type == Yosys::AST::AST_MULTIRANGE) { - // Packed array, e.g. bit [3:0][63:0] a - if (node->children.size() != 1) { - // The Yosys extension for unpacked arrays in packed structs / unions - // only supports memories, i.e. e.g. logic [7:0] a [256] - see above. - struct_array_packing_error(node); - } - width = 1; - for (auto rnode : node->children[0]->children) { - save_struct_range_dimensions(node, rnode); - width *= range_width(node, rnode); - } - // range nodes are now redundant - for (Yosys::AST::AstNode *child : node->children) - delete child; - node->children.clear(); - } - else if (node->range_left < 0) { - // 1 bit signal: bit, logic or reg - width = 1; - } - else { - // already resolved and compacted - width = node->range_left - node->range_right + 1; - } - if (is_union) { - node->range_right = base_offset; - node->range_left = base_offset + width - 1; - } - else { - node->range_right = base_offset + offset; - node->range_left = base_offset + offset + width - 1; - } - node->range_valid = true; - } - if (is_union) { - // check that all members have the same size - if (packed_width == -1) { - // first member - packed_width = width; - } - else { - if (packed_width != width) { - - log_file_error(node->filename, node->location.first_line, "member %s of a packed union has %d bits, expecting %d\n", node->str.c_str(), width, packed_width); - } - } - } - else { - offset += width; - } - } - return (is_union ? packed_width : offset); -} - -Yosys::AST::AstNode *get_struct_member(const Yosys::AST::AstNode *node) -{ - Yosys::AST::AstNode *member_node; - if (node->attributes.count(ID::wiretype) && (member_node = node->attributes.at(ID::wiretype)) && - (member_node->type == Yosys::AST::AST_STRUCT_ITEM || member_node->type == Yosys::AST::AST_STRUCT || member_node->type == Yosys::AST::AST_UNION)) - { - return member_node; - } - return NULL; -} - -static void add_members_to_scope(Yosys::AST::AstNode *snode, std::string name) -{ - // add all the members in a struct or union to local scope - // in case later referenced in assignments - log_assert(snode->type==Yosys::AST::AST_STRUCT || snode->type==Yosys::AST::AST_UNION); - for (auto *node : snode->children) { - auto member_name = name + "." + node->str; - current_scope[member_name] = node; - if (node->type != Yosys::AST::AST_STRUCT_ITEM) { - // embedded struct or union - add_members_to_scope(node, name + "." + node->str); - } - } -} - -static int get_max_offset(Yosys::AST::AstNode *node) -{ - // get the width from the MS member in the struct - // as members are laid out from left to right in the packed wire - log_assert(node->type==Yosys::AST::AST_STRUCT || node->type==Yosys::AST::AST_UNION); - while (node->type != Yosys::AST::AST_STRUCT_ITEM) { - node = node->children[0]; - } - return node->range_left; -} - -static Yosys::AST::AstNode *make_packed_struct(Yosys::AST::AstNode *template_node, std::string &name) -{ - // create a wire for the packed struct - auto wnode = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE); - wnode->str = name; - wnode->is_logic = true; - wnode->range_valid = true; - wnode->is_signed = template_node->is_signed; - int offset = get_max_offset(template_node); - auto range = make_range(offset, 0); - wnode->children.push_back(range); - // make sure this node is the one in scope for this name - current_scope[name] = wnode; - // add all the struct members to scope under the wire's name - add_members_to_scope(template_node, name); - return wnode; -} - -// check if a node or its children contains an assignment to the given variable -static bool node_contains_assignment_to(const Yosys::AST::AstNode* node, const Yosys::AST::AstNode* var) -{ - if (node->type == Yosys::AST::AST_ASSIGN_EQ || node->type == Yosys::AST::AST_ASSIGN_LE) { - // current node is iteslf an assignment - log_assert(node->children.size() >= 2); - const Yosys::AST::AstNode* lhs = node->children[0]; - if (lhs->type == Yosys::AST::AST_IDENTIFIER && lhs->str == var->str) - return false; - } - for (const Yosys::AST::AstNode* child : node->children) { - // if this child shadows the given variable - if (child != var && child->str == var->str && child->type == Yosys::AST::AST_WIRE) - break; // skip the remainder of this block/scope - // depth-first short circuit - if (!node_contains_assignment_to(child, var)) - return false; - } - return true; -} - -static std::string prefix_id(const std::string &prefix, const std::string &str) -{ - log_assert(!prefix.empty() && (prefix.front() == '$' || prefix.front() == '\\')); - log_assert(!str.empty() && (str.front() == '$' || str.front() == '\\')); - log_assert(prefix.back() == '.'); - if (str.front() == '\\') - return prefix + str.substr(1); - return prefix + str; -} - -// returns whether an expression contains an unbased unsized literal; does not -// check the literal exists in a self-determined context -static bool contains_unbased_unsized(const Yosys::AST::AstNode *node) -{ - if (node->type == Yosys::AST::AST_CONSTANT) - return node->is_unsized; - for (const Yosys::AST::AstNode *child : node->children) - if (contains_unbased_unsized(child)) - return true; - return false; -} - -// adds a wire to the current module with the given name that matches the -// dimensions of the given wire reference -void add_wire_for_ref(const RTLIL::Wire *ref, const std::string &str) -{ - Yosys::AST::AstNode *left = Yosys::AST::AstNode::mkconst_int(ref->width - 1 + ref->start_offset, true); - Yosys::AST::AstNode *right = Yosys::AST::AstNode::mkconst_int(ref->start_offset, true); - if (ref->upto) - std::swap(left, right); - Yosys::AST::AstNode *range = new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, left, right); - - Yosys::AST::AstNode *wire = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE, range); - wire->is_signed = ref->is_signed; - wire->is_logic = true; - wire->str = str; - - current_ast_mod->children.push_back(wire); - current_scope[str] = wire; -} - -enum class IdentUsage { - NotReferenced, // target variable is neither read or written in the block - Assigned, // target variable is always assigned before use - SyncRequired, // target variable may be used before it has been assigned -}; - -// determines whether a local variable a block is always assigned before it is -// used, meaning the nosync attribute can automatically be added to that -// variable -static IdentUsage always_asgn_before_use(const Yosys::AST::AstNode *node, const std::string &target) -{ - // This variable has been referenced before it has necessarily been assigned - // a value in this procedure. - if (node->type == Yosys::AST::AST_IDENTIFIER && node->str == target) - return IdentUsage::SyncRequired; - - // For case statements (which are also used for if/else), we check each - // possible branch. If the variable is assigned in all branches, then it is - // assigned, and a sync isn't required. If it used before assignment in any - // branch, then a sync is required. - if (node->type == Yosys::AST::AST_CASE) { - bool all_defined = true; - bool any_used = false; - bool has_default = false; - for (const Yosys::AST::AstNode *child : node->children) { - if (child->type == Yosys::AST::AST_COND && child->children.at(0)->type == Yosys::AST::AST_DEFAULT) - has_default = true; - IdentUsage nested = always_asgn_before_use(child, target); - if (nested != IdentUsage::Assigned && child->type == Yosys::AST::AST_COND) - all_defined = false; - if (nested == IdentUsage::SyncRequired) - any_used = true; - } - if (any_used) - return IdentUsage::SyncRequired; - else if (all_defined && has_default) - return IdentUsage::Assigned; - else - return IdentUsage::NotReferenced; - } - - // Check if this is an assignment to the target variable. For simplicity, we - // don't analyze sub-ranges of the variable. - if (node->type == Yosys::AST::AST_ASSIGN_EQ) { - const Yosys::AST::AstNode *ident = node->children.at(0); - if (ident->type == Yosys::AST::AST_IDENTIFIER && ident->str == target) - return IdentUsage::Assigned; - } - - for (const Yosys::AST::AstNode *child : node->children) { - IdentUsage nested = always_asgn_before_use(child, target); - if (nested != IdentUsage::NotReferenced) - return nested; - } - return IdentUsage::NotReferenced; -} - -static const std::string auto_nosync_prefix = "\\AutoNosync"; - -// mark a local variable in an always_comb block for automatic nosync -// consideration -static void mark_auto_nosync(Yosys::AST::AstNode *block, const Yosys::AST::AstNode *wire) -{ - log_assert(block->type == Yosys::AST::AST_BLOCK); - log_assert(wire->type == Yosys::AST::AST_WIRE); - block->attributes[auto_nosync_prefix + wire->str] = Yosys::AST::AstNode::mkconst_int(1, - false); -} - -// block names can be prefixed with an explicit scope during elaboration -static bool is_autonamed_block(const std::string &str) { - size_t last_dot = str.rfind('.'); - // unprefixed names: autonamed if the first char is a dollar sign - if (last_dot == std::string::npos) - return str.at(0) == '$'; // e.g., `$fordecl_block$1` - // prefixed names: autonamed if the final chunk begins with a dollar sign - return str.rfind(".$") == last_dot; // e.g., `\foo.bar.$fordecl_block$1` -} - -// check a procedural block for auto-nosync markings, remove them, and add -// nosync to local variables as necessary -static void check_auto_nosync(Yosys::AST::AstNode *node) -{ - std::vector attrs_to_drop; - for (const auto& elem : node->attributes) { - // skip attributes that don't begin with the prefix - if (elem.first.compare(0, auto_nosync_prefix.size(), - auto_nosync_prefix.c_str())) - continue; - - // delete and remove the attribute once we're done iterating - attrs_to_drop.push_back(elem.first); - - // find the wire based on the attribute - std::string wire_name = elem.first.substr(auto_nosync_prefix.size()); - auto it = current_scope.find(wire_name); - if (it == current_scope.end()) - continue; - - // analyze the usage of the local variable in this block - IdentUsage ident_usage = always_asgn_before_use(node, wire_name); - if (ident_usage != IdentUsage::Assigned) - continue; - - // mark the wire with `nosync` - Yosys::AST::AstNode *wire = it->second; - log_assert(wire->type == Yosys::AST::AST_WIRE); - wire->attributes[ID::nosync] = node->mkconst_int(1, false); - } - - // remove the attributes we've "consumed" - for (const RTLIL::IdString &str : attrs_to_drop) { - auto it = node->attributes.find(str); - delete it->second; - node->attributes.erase(it); - } - - // check local variables in any nested blocks - for (Yosys::AST::AstNode *child : node->children) - check_auto_nosync(child); -} - -static inline std::string encode_filename(const std::string &filename) -{ - std::stringstream val; - if (!std::any_of(filename.begin(), filename.end(), [](char c) { - return static_cast(c) < 33 || static_cast(c) > 126; - })) return filename; - for (unsigned char const c : filename) { - if (c < 33 || c > 126) - val << stringf("$%02x", c); - else - val << c; - } - return val.str(); -} - -// convert the AST into a simpler AST that has all parameters substituted by their -// values, unrolled for-loops, expanded generate blocks, etc. when this function -// is done with an AST it can be converted into RTLIL using genRTLIL(). -// -// this function also does all name resolving and sets the id2ast member of all -// nodes that link to a different node using names and lexical scoping. -bool simplify(Yosys::AST::AstNode *ast_node, bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param) -{ - static int recursion_counter = 0; - static bool deep_recursion_warning = false; - - if (recursion_counter++ == 1000 && deep_recursion_warning) { - log_warning("Deep recursion in AST simplifier.\nDoes this design contain overly long or deeply nested expressions, or excessive recursion?\n"); - deep_recursion_warning = false; - } - - static bool unevaluated_tern_branch = false; - - Yosys::AST::AstNode *newNode = NULL; - bool did_something = false; - -#if 0 - log("-------------\n"); - log("AST simplify[%d] depth %d at %s:%d on %s %p:\n", stage, recursion_counter, filename.c_str(), location.first_line, type2str(type).c_str(), this); - log("const_fold=%d, at_zero=%d, in_lvalue=%d, stage=%d, width_hint=%d, sign_hint=%d, in_param=%d\n", - int(const_fold), int(at_zero), int(in_lvalue), int(stage), int(width_hint), int(sign_hint), int(in_param)); - // dumpAst(NULL, "> "); -#endif - - if (stage == 0) - { - log_assert(ast_node->type == Yosys::AST::AST_MODULE || ast_node->type == Yosys::AST::AST_INTERFACE); - - deep_recursion_warning = true; - while (simplify(ast_node, const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { } - - if (!flag_nomem2reg && !ast_node->get_bool_attribute(ID::nomem2reg)) - { - dict> mem2reg_places; - dict mem2reg_candidates, dummy_proc_flags; - uint32_t flags = flag_mem2reg ? Yosys::AST::AstNode::MEM2REG_FL_ALL : 0; - ast_node->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, dummy_proc_flags, flags); // not sure if correct MAND - - pool mem2reg_set; - for (auto &it : mem2reg_candidates) - { - Yosys::AST::AstNode *mem = it.first; - uint32_t memflags = it.second; - bool this_nomeminit = flag_nomeminit; - log_assert((memflags & ~0x00ffff00) == 0); - - if (mem->get_bool_attribute(ID::nomem2reg)) - continue; - - if (mem->get_bool_attribute(ID::nomeminit) || ast_node->get_bool_attribute(ID::nomeminit)) - this_nomeminit = true; - - if (memflags & Yosys::AST::AstNode::MEM2REG_FL_FORCED) - goto silent_activate; - - if (memflags & Yosys::AST::AstNode::MEM2REG_FL_EQ2) - goto verbose_activate; - - if (memflags & Yosys::AST::AstNode::MEM2REG_FL_SET_ASYNC) - goto verbose_activate; - - if ((memflags & Yosys::AST::AstNode::MEM2REG_FL_SET_INIT) && (memflags & Yosys::AST::AstNode::MEM2REG_FL_SET_ELSE) && this_nomeminit) - goto verbose_activate; - - if (memflags & Yosys::AST::AstNode::MEM2REG_FL_CMPLX_LHS) - goto verbose_activate; - - if ((memflags & Yosys::AST::AstNode::MEM2REG_FL_CONST_LHS) && !(memflags & Yosys::AST::AstNode::MEM2REG_FL_VAR_LHS)) - goto verbose_activate; - - // log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags)); - continue; - - verbose_activate: - if (mem2reg_set.count(mem) == 0) { - std::string message = stringf("Replacing memory %s with list of registers.", mem->str.c_str()); - bool first_element = true; - for (auto &place : mem2reg_places[it.first]) { - message += stringf("%s%s", first_element ? " See " : ", ", place.c_str()); - first_element = false; - } - log_warning("%s\n", message.c_str()); - } - - silent_activate: - // log("Note: Replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags)); - mem2reg_set.insert(mem); - } - - for (auto node : mem2reg_set) - { - int mem_width, mem_size, addr_bits; - node->meminfo(mem_width, mem_size, addr_bits); - - int data_range_left = node->children[0]->range_left; - int data_range_right = node->children[0]->range_right; - - if (node->children[0]->range_swapped) - std::swap(data_range_left, data_range_right); - - for (int i = 0; i < mem_size; i++) { - Yosys::AST::AstNode *reg = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE, new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, - node->mkconst_int(data_range_left, true), node->mkconst_int(data_range_right, true))); - reg->str = stringf("%s[%d]", node->str.c_str(), i); - reg->is_reg = true; - reg->is_signed = node->is_signed; - for (auto &it : node->attributes) - if (it.first != ID::mem2reg) - reg->attributes.emplace(it.first, it.second->clone()); - reg->filename = node->filename; - reg->location = node->location; - ast_node->children.push_back(reg); - while (simplify(reg, true, false, false, 1, -1, false, false)) { } // <- not sure about reg being the first arg here... - } - } - - Yosys::AST::AstNode *async_block = NULL; - while (ast_node->mem2reg_as_needed_pass2(mem2reg_set, ast_node, NULL, async_block)) { } - - vector delnodes; - ast_node->mem2reg_remove(mem2reg_set, delnodes); - - for (auto node : delnodes) - delete node; - } - - while (simplify(ast_node, const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint, in_param)) { } - recursion_counter--; - return false; - } - - Yosys::AST::current_filename = ast_node->filename; - - // we do not look inside a task or function - // (but as soon as a task or function is instantiated we process the generated AST as usual) - if (ast_node->type == Yosys::AST::AST_FUNCTION || ast_node->type == Yosys::AST::AST_TASK) { - recursion_counter--; - return false; - } - - // deactivate all calls to non-synthesis system tasks - // note that $display, $finish, and $stop are used for synthesis-time DRC so they're not in this list - if ((ast_node->type == Yosys::AST::AST_FCALL || ast_node->type == Yosys::AST::AST_TCALL) && (ast_node->str == "$strobe" || ast_node->str == "$monitor" || ast_node->str == "$time" || - ast_node->str == "$dumpfile" || ast_node->str == "$dumpvars" || ast_node->str == "$dumpon" || ast_node->str == "$dumpoff" || ast_node->str == "$dumpall")) { - log_file_warning(ast_node->filename, ast_node->location.first_line, "Ignoring call to system %s %s.\n", ast_node->type == Yosys::AST::AST_FCALL ? "function" : "task", ast_node->str.c_str()); - ast_node->delete_children(); - ast_node->str = std::string(); - } - - if ((ast_node->type == Yosys::AST::AST_TCALL) && (ast_node->str == "$display" || ast_node->str == "$write") && (!current_always || current_always->type != Yosys::AST::AST_INITIAL)) { - log_file_warning(ast_node->filename, ast_node->location.first_line, "System task `%s' outside initial block is unsupported.\n", ast_node->str.c_str()); - ast_node->delete_children(); - ast_node->str = std::string(); - } - - // print messages if this a call to $display() or $write() - // This code implements only a small subset of Verilog-2005 $display() format specifiers, - // but should be good enough for most uses - if ((ast_node->type == Yosys::AST::AST_TCALL) && ((ast_node->str == "$display") || (ast_node->str == "$write"))) - { - int nargs = GetSize(ast_node->children); - if (nargs < 1) - log_file_error(ast_node->filename, ast_node->location.first_line, "System task `%s' got %d arguments, expected >= 1.\n", - ast_node->str.c_str(), int(ast_node->children.size())); - - // First argument is the format string - Yosys::AST::AstNode *node_string = ast_node->children[0]; - while (simplify(node_string, true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_string->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system task `%s' with non-constant 1st argument.\n", ast_node->str.c_str()); - std::string sformat = node_string->bitsAsConst().decode_string(); - std::string sout = ast_node->process_format_str(sformat, 1, stage, width_hint, sign_hint); - // Finally, print the message (only include a \n for $display, not for $write) - log("%s", sout.c_str()); - if (ast_node->str == "$display") - log("\n"); - ast_node->delete_children(); - ast_node->str = std::string(); - } - - // activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.) - if (ast_node->type == Yosys::AST::AST_WIRE || ast_node->type == Yosys::AST::AST_PARAMETER || ast_node->type == Yosys::AST::AST_LOCALPARAM || ast_node->type == Yosys::AST::AST_ENUM_ITEM || ast_node->type == Yosys::AST::AST_DEFPARAM || ast_node->type == Yosys::AST::AST_PARASET || ast_node->type == Yosys::AST::AST_RANGE || ast_node->type == Yosys::AST::AST_PREFIX || ast_node->type == Yosys::AST::AST_TYPEDEF) - const_fold = true; - if (ast_node->type == Yosys::AST::AST_IDENTIFIER && current_scope.count(ast_node->str) > 0 && (current_scope[ast_node->str]->type == Yosys::AST::AST_PARAMETER || current_scope[ast_node->str]->type == Yosys::AST::AST_LOCALPARAM || current_scope[ast_node->str]->type == Yosys::AST::AST_ENUM_ITEM)) - const_fold = true; - - // in certain cases a function must be evaluated constant. this is what in_param controls. - if (ast_node->type == Yosys::AST::AST_PARAMETER || ast_node->type == Yosys::AST::AST_LOCALPARAM || ast_node->type == Yosys::AST::AST_DEFPARAM || ast_node->type == Yosys::AST::AST_PARASET || ast_node->type == Yosys::AST::AST_PREFIX) - in_param = true; - - std::map backup_scope; - - // create name resolution entries for all objects with names - // also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;") - if (ast_node->type == Yosys::AST::AST_MODULE || ast_node->type == Yosys::AST::AST_INTERFACE) { - current_scope.clear(); - std::set existing; - int counter = 0; - ast_node->label_genblks(existing, counter); - std::map this_wire_scope; - for (size_t i = 0; i < ast_node->children.size(); i++) { - Yosys::AST::AstNode *node = ast_node->children[i]; - - if (node->type == Yosys::AST::AST_WIRE) { - if (node->children.size() == 1 && node->children[0]->type == Yosys::AST::AST_RANGE) { - for (auto c : node->children[0]->children) { - if (!c->is_simple_const_expr()) { - if (ast_node->attributes.count(ID::dynports)) - delete ast_node->attributes.at(ID::dynports); - node->attributes[ID::dynports] = node->mkconst_int(1, true); - } - } - } - if (this_wire_scope.count(node->str) > 0) { - Yosys::AST::AstNode *first_node = this_wire_scope[node->str]; - if (first_node->is_input && node->is_reg) - goto wires_are_incompatible; - if (!node->is_input && !node->is_output && node->is_reg && node->children.size() == 0) - goto wires_are_compatible; - if (first_node->children.size() == 0 && node->children.size() == 1 && node->children[0]->type == Yosys::AST::AST_RANGE) { - Yosys::AST::AstNode *r = node->children[0]; - if (r->range_valid && r->range_left == 0 && r->range_right == 0) { - delete r; - node->children.pop_back(); - } - } - if (first_node->children.size() != node->children.size()) - goto wires_are_incompatible; - for (size_t j = 0; j < node->children.size(); j++) { - Yosys::AST::AstNode *n1 = first_node->children[j], *n2 = node->children[j]; - if (n1->type == Yosys::AST::AST_RANGE && n2->type == Yosys::AST::AST_RANGE && n1->range_valid && n2->range_valid) { - if (n1->range_left != n2->range_left) - goto wires_are_incompatible; - if (n1->range_right != n2->range_right) - goto wires_are_incompatible; - } else if (*n1 != *n2) - goto wires_are_incompatible; - } - if (first_node->range_left != node->range_left) - goto wires_are_incompatible; - if (first_node->range_right != node->range_right) - goto wires_are_incompatible; - if (first_node->port_id == 0 && (node->is_input || node->is_output)) - goto wires_are_incompatible; - wires_are_compatible: - if (node->is_input) - first_node->is_input = true; - if (node->is_output) - first_node->is_output = true; - if (node->is_reg) - first_node->is_reg = true; - if (node->is_logic) - first_node->is_logic = true; - if (node->is_signed) - first_node->is_signed = true; - for (auto &it : node->attributes) { - if (first_node->attributes.count(it.first) > 0) - delete first_node->attributes[it.first]; - first_node->attributes[it.first] = it.second->clone(); - } - ast_node->children.erase(ast_node->children.begin()+(i--)); - did_something = true; - delete node; - continue; - wires_are_incompatible: - if (stage > 1) - log_file_error(ast_node->filename, ast_node->location.first_line, "Incompatible re-declaration of wire %s.\n", node->str.c_str()); - continue; - } - this_wire_scope[node->str] = node; - } - // these nodes appear at the top level in a module and can define names - if (node->type == Yosys::AST::AST_PARAMETER || node->type == Yosys::AST::AST_LOCALPARAM || node->type == Yosys::AST::AST_WIRE || node->type == Yosys::AST::AST_AUTOWIRE || node->type == Yosys::AST::AST_GENVAR || - node->type == Yosys::AST::AST_MEMORY || node->type == Yosys::AST::AST_FUNCTION || node->type == Yosys::AST::AST_TASK || node->type == Yosys::AST::AST_DPI_FUNCTION || node->type == Yosys::AST::AST_CELL || - node->type == Yosys::AST::AST_TYPEDEF) { - backup_scope[node->str] = current_scope[node->str]; - current_scope[node->str] = node; - } - if (node->type == Yosys::AST::AST_ENUM) { - current_scope[node->str] = node; - for (auto enode : node->children) { - log_assert(enode->type==Yosys::AST::AST_ENUM_ITEM); - if (current_scope.count(enode->str) == 0) - current_scope[enode->str] = enode; - else - log_file_error(ast_node->filename, ast_node->location.first_line, "enum item %s already exists\n", enode->str.c_str()); - } - } - } - for (size_t i = 0; i < ast_node->children.size(); i++) { - Yosys::AST::AstNode *node = ast_node->children[i]; - if (node->type == Yosys::AST::AST_PARAMETER || node->type == Yosys::AST::AST_LOCALPARAM || node->type == Yosys::AST::AST_WIRE || node->type == Yosys::AST::AST_AUTOWIRE || node->type == Yosys::AST::AST_MEMORY || node->type == Yosys::AST::AST_TYPEDEF) - while (simplify(node, true, false, false, 1, -1, false, node->type == Yosys::AST::AST_PARAMETER || node->type == Yosys::AST::AST_LOCALPARAM)) - did_something = true; - if (node->type == Yosys::AST::AST_ENUM) { - for (auto enode : node->children){ - log_assert(enode->type==Yosys::AST::AST_ENUM_ITEM); - while (simplify(node, true, false, false, 1, -1, false, in_param)) - did_something = true; - } - } - } - - for (Yosys::AST::AstNode *child : ast_node->children) - if (child->type == Yosys::AST::AST_ALWAYS && - child->attributes.count(ID::always_comb)) - check_auto_nosync(child); - } - - // create name resolution entries for all objects with names - if (ast_node->type == Yosys::AST::AST_PACKAGE) { - //add names to package scope - for (size_t i = 0; i < ast_node->children.size(); i++) { - Yosys::AST::AstNode *node = ast_node->children[i]; - // these nodes appear at the top level in a package and can define names - if (node->type == Yosys::AST::AST_PARAMETER || node->type == Yosys::AST::AST_LOCALPARAM || node->type == Yosys::AST::AST_TYPEDEF || node->type == Yosys::AST::AST_FUNCTION || node->type == Yosys::AST::AST_TASK) { - current_scope[node->str] = node; - } - if (node->type == Yosys::AST::AST_ENUM) { - current_scope[node->str] = node; - for (auto enode : node->children) { - log_assert(enode->type==Yosys::AST::AST_ENUM_ITEM); - if (current_scope.count(enode->str) == 0) - current_scope[enode->str] = enode; - else - log_file_error(ast_node->filename, ast_node->location.first_line, "enum item %s already exists in package\n", enode->str.c_str()); - } - } - } - } - - - auto backup_current_block = current_block; - auto backup_current_block_child = current_block_child; - auto backup_current_top_block = current_top_block; - auto backup_current_always = current_always; - auto backup_current_always_clocked = current_always_clocked; - - if (ast_node->type == Yosys::AST::AST_ALWAYS || ast_node->type == Yosys::AST::AST_INITIAL) - { - if (current_always != nullptr) - log_file_error(ast_node->filename, ast_node->location.first_line, "Invalid nesting of always blocks and/or initializations.\n"); - - current_always = ast_node; - current_always_clocked = false; - - if (ast_node->type == Yosys::AST::AST_ALWAYS) - for (auto child : ast_node->children) { - if (child->type == Yosys::AST::AST_POSEDGE || child->type == Yosys::AST::AST_NEGEDGE) - current_always_clocked = true; - if (child->type == Yosys::AST::AST_EDGE && GetSize(child->children) == 1 && - child->children[0]->type == Yosys::AST::AST_IDENTIFIER && child->children[0]->str == "\\$global_clock") - current_always_clocked = true; - } - } - - if (ast_node->type == Yosys::AST::AST_CELL) { - bool lookup_suggested = false; - - for (Yosys::AST::AstNode *child : ast_node->children) { - // simplify any parameters to constants - if (child->type == Yosys::AST::AST_PARASET) - while (simplify(child, true, false, false, 1, -1, false, true)) { } - - // look for patterns which _may_ indicate ambiguity requiring - // resolution of the underlying module - if (child->type == Yosys::AST::AST_ARGUMENT) { - if (child->children.size() != 1) - continue; - const Yosys::AST::AstNode *value = child->children[0]; - if (value->type == Yosys::AST::AST_IDENTIFIER) { - const Yosys::AST::AstNode *elem = value->id2ast; - if (elem == nullptr) { - if (current_scope.count(value->str)) - elem = current_scope.at(value->str); - else - continue; - } - if (elem->type == Yosys::AST::AST_MEMORY) - // need to determine is the is a read or wire - lookup_suggested = true; - else if (elem->type == Yosys::AST::AST_WIRE && elem->is_signed && !value->children.empty()) - // this may be a fully sliced signed wire which needs - // to be indirected to produce an unsigned connection - lookup_suggested = true; - } - else if (contains_unbased_unsized(value)) - // unbased unsized literals extend to width of the context - lookup_suggested = true; - } - } - - const RTLIL::Module *module = nullptr; - if (lookup_suggested) - module = ast_node->lookup_cell_module(); - if (module) { - size_t port_counter = 0; - for (Yosys::AST::AstNode *child : ast_node->children) { - if (child->type != Yosys::AST::AST_ARGUMENT) - continue; - - // determine the full name of port this argument is connected to - RTLIL::IdString port_name; - if (child->str.size()) - port_name = child->str; - else { - if (port_counter >= module->ports.size()) - log_file_error(ast_node->filename, ast_node->location.first_line, - "Cell instance has more ports than the module!\n"); - port_name = module->ports[port_counter++]; - } - - // find the port's wire in the underlying module - const RTLIL::Wire *ref = module->wire(port_name); - if (ref == nullptr) - log_file_error(ast_node->filename, ast_node->location.first_line, - "Cell instance refers to port %s which does not exist in module %s!.\n", - log_id(port_name), log_id(module->name)); - - // select the argument, if present - log_assert(child->children.size() <= 1); - if (child->children.empty()) - continue; - Yosys::AST::AstNode *arg = child->children[0]; - - // plain identifiers never need indirection; this also prevents - // adding infinite levels of indirection - if (arg->type == Yosys::AST::AST_IDENTIFIER && arg->children.empty()) - continue; - - // only add indirection for standard inputs or outputs - if (ref->port_input == ref->port_output) - continue; - - did_something = true; - - // create the indirection wire - std::stringstream sstr; - sstr << "$indirect$" << ref->name.c_str() << "$" << encode_filename(ast_node->filename) << ":" << ast_node->location.first_line << "$" << (autoidx++); - std::string tmp_str = sstr.str(); - add_wire_for_ref(ref, tmp_str); - - Yosys::AST::AstNode *asgn = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN); - current_ast_mod->children.push_back(asgn); - - Yosys::AST::AstNode *ident = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - ident->str = tmp_str; - child->children[0] = ident->clone(); - - if (ref->port_input && !ref->port_output) { - asgn->children.push_back(ident); - asgn->children.push_back(arg); - } else { - log_assert(!ref->port_input && ref->port_output); - asgn->children.push_back(arg); - asgn->children.push_back(ident); - } - } - - - } - } - - int backup_width_hint = width_hint; - bool backup_sign_hint = sign_hint; - - bool detect_width_simple = false; - bool child_0_is_self_determined = false; - bool child_1_is_self_determined = false; - bool child_2_is_self_determined = false; - bool children_are_self_determined = false; - bool reset_width_after_children = false; - - switch (ast_node->type) - { - case Yosys::AST::AST_ASSIGN_EQ: - case Yosys::AST::AST_ASSIGN_LE: - case Yosys::AST::AST_ASSIGN: - while (!ast_node->children[0]->basic_prep && simplify(ast_node->children[0], false, false, true, stage, -1, false, in_param) == true) - did_something = true; - while (!ast_node->children[1]->basic_prep && simplify(ast_node->children[1], false, false, false, stage, -1, false, in_param) == true) - did_something = true; - ast_node->children[0]->detectSignWidth(backup_width_hint, backup_sign_hint); - ast_node->children[1]->detectSignWidth(width_hint, sign_hint); - width_hint = max(width_hint, backup_width_hint); - child_0_is_self_determined = true; - // test only once, before optimizations and memory mappings but after assignment LHS was mapped to an identifier - if (ast_node->children[0]->id2ast && !ast_node->children[0]->was_checked) { - if ((ast_node->type == Yosys::AST::AST_ASSIGN_LE || ast_node->type == Yosys::AST::AST_ASSIGN_EQ) && ast_node->children[0]->id2ast->is_logic) - ast_node->children[0]->id2ast->is_reg = true; // if logic type is used in a block asignment - if ((ast_node->type == Yosys::AST::AST_ASSIGN_LE || ast_node->type == Yosys::AST::AST_ASSIGN_EQ) && !ast_node->children[0]->id2ast->is_reg) - log_warning("wire '%s' is assigned in a block at %s.\n", ast_node->children[0]->str.c_str(), ast_node->loc_string().c_str()); - if (ast_node->type == Yosys::AST::AST_ASSIGN && ast_node->children[0]->id2ast->is_reg) { - bool is_rand_reg = false; - if (ast_node->children[1]->type == Yosys::AST::AST_FCALL) { - if (ast_node->children[1]->str == "\\$anyconst") - is_rand_reg = true; - if (ast_node->children[1]->str == "\\$anyseq") - is_rand_reg = true; - if (ast_node->children[1]->str == "\\$allconst") - is_rand_reg = true; - if (ast_node->children[1]->str == "\\$allseq") - is_rand_reg = true; - } - if (!is_rand_reg) - log_warning("reg '%s' is assigned in a continuous assignment at %s.\n", ast_node->children[0]->str.c_str(), ast_node->loc_string().c_str()); - } - ast_node->children[0]->was_checked = true; - } - break; - - case Yosys::AST::AST_STRUCT: - case Yosys::AST::AST_UNION: - if (!ast_node->basic_prep) { - for (auto *node : ast_node->children) { - // resolve any ranges - while (!node->basic_prep && simplify(node, true, false, false, stage, -1, false, false)) { - did_something = true; - } - } - // determine member offsets and widths - size_packed_struct(ast_node, 0); - - // instance rather than just a type in a typedef or outer struct? - if (!ast_node->str.empty() && ast_node->str[0] == '\\') { - // instance so add a wire for the packed structure - auto wnode = make_packed_struct(ast_node, ast_node->str); - log_assert(current_ast_mod); - current_ast_mod->children.push_back(wnode); - } - ast_node->basic_prep = true; - } - break; - - case Yosys::AST::AST_STRUCT_ITEM: - break; - - case Yosys::AST::AST_ENUM: - //log("\nENUM %s: %d child %d\n", ast_node->str.c_str(), ast_node->basic_prep, ast_node->children[0]->basic_prep); - if (!ast_node->basic_prep) { - for (auto item_node : ast_node->children) { - while (!item_node->basic_prep && simplify(item_node, false, false, false, stage, -1, false, in_param)) - did_something = true; - } - // allocate values (called more than once) - ast_node->allocateDefaultEnumValues(); - } - break; - - case Yosys::AST::AST_PARAMETER: - case Yosys::AST::AST_LOCALPARAM: - // if parameter is implicit type which is the typename of a struct or union, - // save information about struct in wiretype attribute - if (ast_node->children[0]->type == Yosys::AST::AST_IDENTIFIER && current_scope.count(ast_node->children[0]->str) > 0) { - auto item_node = current_scope[ast_node->children[0]->str]; - if (item_node->type == Yosys::AST::AST_STRUCT || item_node->type == Yosys::AST::AST_UNION) { - ast_node->attributes[ID::wiretype] = item_node->clone(); - size_packed_struct(ast_node->attributes[ID::wiretype], 0); - add_members_to_scope(ast_node->attributes[ID::wiretype], ast_node->str); - } - } - while (!ast_node->children[0]->basic_prep && simplify(ast_node->children[0], false, false, false, stage, -1, false, true) == true) - did_something = true; - ast_node->children[0]->detectSignWidth(width_hint, sign_hint); - if (ast_node->children.size() > 1 && ast_node->children[1]->type == Yosys::AST::AST_RANGE) { - while (!ast_node->children[1]->basic_prep && simplify(ast_node->children[1], false, false, false, stage, -1, false, true) == true) - did_something = true; - if (!ast_node->children[1]->range_valid) - log_file_error(ast_node->filename, ast_node->location.first_line, "Non-constant width range on parameter decl.\n"); - width_hint = max(width_hint, ast_node->children[1]->range_left - ast_node->children[1]->range_right + 1); - } - break; - case Yosys::AST::AST_ENUM_ITEM: - while (!ast_node->children[0]->basic_prep && simplify(ast_node->children[0], false, false, false, stage, -1, false, in_param)) - did_something = true; - ast_node->children[0]->detectSignWidth(width_hint, sign_hint); - if (ast_node->children.size() > 1 && ast_node->children[1]->type == Yosys::AST::AST_RANGE) { - while (!ast_node->children[1]->basic_prep && simplify(ast_node->children[1], false, false, false, stage, -1, false, in_param)) - did_something = true; - if (!ast_node->children[1]->range_valid) - log_file_error(ast_node->filename, ast_node->location.first_line, "Non-constant width range on enum item decl.\n"); - width_hint = max(width_hint, ast_node->children[1]->range_left - ast_node->children[1]->range_right + 1); - } - break; - - case Yosys::AST::AST_TO_BITS: - case Yosys::AST::AST_TO_SIGNED: - case Yosys::AST::AST_TO_UNSIGNED: - case Yosys::AST::AST_SELFSZ: - case Yosys::AST::AST_CAST_SIZE: - case Yosys::AST::AST_CONCAT: - case Yosys::AST::AST_REPLICATE: - case Yosys::AST::AST_REDUCE_AND: - case Yosys::AST::AST_REDUCE_OR: - case Yosys::AST::AST_REDUCE_XOR: - case Yosys::AST::AST_REDUCE_XNOR: - case Yosys::AST::AST_REDUCE_BOOL: - detect_width_simple = true; - children_are_self_determined = true; - break; - - case Yosys::AST::AST_NEG: - case Yosys::AST::AST_BIT_NOT: - case Yosys::AST::AST_POS: - case Yosys::AST::AST_BIT_AND: - case Yosys::AST::AST_BIT_OR: - case Yosys::AST::AST_BIT_XOR: - case Yosys::AST::AST_BIT_XNOR: - case Yosys::AST::AST_ADD: - case Yosys::AST::AST_SUB: - case Yosys::AST::AST_MUL: - case Yosys::AST::AST_DIV: - case Yosys::AST::AST_MOD: - detect_width_simple = true; - break; - - case Yosys::AST::AST_SHIFT_LEFT: - case Yosys::AST::AST_SHIFT_RIGHT: - case Yosys::AST::AST_SHIFT_SLEFT: - case Yosys::AST::AST_SHIFT_SRIGHT: - case Yosys::AST::AST_POW: - detect_width_simple = true; - child_1_is_self_determined = true; - break; - - case Yosys::AST::AST_LT: - case Yosys::AST::AST_LE: - case Yosys::AST::AST_EQ: - case Yosys::AST::AST_NE: - case Yosys::AST::AST_EQX: - case Yosys::AST::AST_NEX: - case Yosys::AST::AST_GE: - case Yosys::AST::AST_GT: - width_hint = -1; - sign_hint = true; - for (auto child : ast_node->children) { - while (!child->basic_prep && simplify(child, false, false, in_lvalue, stage, -1, false, in_param) == true) - did_something = true; - child->detectSignWidthWorker(width_hint, sign_hint); - } - reset_width_after_children = true; - break; - - case Yosys::AST::AST_LOGIC_AND: - case Yosys::AST::AST_LOGIC_OR: - case Yosys::AST::AST_LOGIC_NOT: - detect_width_simple = true; - children_are_self_determined = true; - break; - - case Yosys::AST::AST_TERNARY: - child_0_is_self_determined = true; - break; - - case Yosys::AST::AST_MEMRD: - detect_width_simple = true; - children_are_self_determined = true; - break; - - case Yosys::AST::AST_FCALL: - case Yosys::AST::AST_TCALL: - children_are_self_determined = true; - break; - - default: - width_hint = -1; - sign_hint = false; - } - - if (detect_width_simple && width_hint < 0) { - if (ast_node->type == Yosys::AST::AST_REPLICATE) - while (simplify(ast_node->children[0], true, false, in_lvalue, stage, -1, false, true) == true) - did_something = true; - for (auto child : ast_node->children) - while (!child->basic_prep && simplify(child, false, false, in_lvalue, stage, -1, false, in_param) == true) - did_something = true; - ast_node->detectSignWidth(width_hint, sign_hint); - } - - if (ast_node->type == Yosys::AST::AST_FCALL && ast_node->str == "\\$past") - ast_node->detectSignWidth(width_hint, sign_hint); - - if (ast_node->type == Yosys::AST::AST_TERNARY) { - if (width_hint < 0) { - while (!ast_node->children[0]->basic_prep && simplify(ast_node->children[0], true, false, in_lvalue, stage, -1, false, in_param)) - did_something = true; - - bool backup_unevaluated_tern_branch = unevaluated_tern_branch; - Yosys::AST::AstNode *chosen = ast_node->get_tern_choice().first; - - unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == ast_node->children[2]; - while (!ast_node->children[1]->basic_prep && simplify(ast_node->children[1], false, false, in_lvalue, stage, -1, false, in_param)) - did_something = true; - - unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == ast_node->children[1]; - while (!ast_node->children[2]->basic_prep && simplify(ast_node->children[2], false, false, in_lvalue, stage, -1, false, in_param)) - did_something = true; - - unevaluated_tern_branch = backup_unevaluated_tern_branch; - ast_node->detectSignWidth(width_hint, sign_hint); - } - int width_hint_left, width_hint_right; - bool sign_hint_left, sign_hint_right; - bool found_real_left, found_real_right; - ast_node->children[1]->detectSignWidth(width_hint_left, sign_hint_left, &found_real_left); - ast_node->children[2]->detectSignWidth(width_hint_right, sign_hint_right, &found_real_right); - if (found_real_left || found_real_right) { - child_1_is_self_determined = true; - child_2_is_self_determined = true; - } - } - - if (ast_node->type == Yosys::AST::AST_CONDX && ast_node->children.size() > 0 && ast_node->children.at(0)->type == Yosys::AST::AST_CONSTANT) { - for (auto &bit : ast_node->children.at(0)->bits) - if (bit == State::Sz || bit == State::Sx) - bit = State::Sa; - } - - if (ast_node->type == Yosys::AST::AST_CONDZ && ast_node->children.size() > 0 && ast_node->children.at(0)->type == Yosys::AST::AST_CONSTANT) { - for (auto &bit : ast_node->children.at(0)->bits) - if (bit == State::Sz) - bit = State::Sa; - } - - if (const_fold && ast_node->type == Yosys::AST::AST_CASE) - { - ast_node->detectSignWidth(width_hint, sign_hint); - while (simplify(ast_node->children[0], const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT && ast_node->children[0]->bits_only_01()) { - ast_node->children[0]->is_signed = sign_hint; - RTLIL::Const case_expr = ast_node->children[0]->bitsAsConst(width_hint, sign_hint); - std::vector new_children; - new_children.push_back(ast_node->children[0]); - for (int i = 1; i < GetSize(ast_node->children); i++) { - Yosys::AST::AstNode *child = ast_node->children[i]; - log_assert(child->type == Yosys::AST::AST_COND || child->type == Yosys::AST::AST_CONDX || child->type == Yosys::AST::AST_CONDZ); - for (auto v : child->children) { - if (v->type == Yosys::AST::AST_DEFAULT) - goto keep_const_cond; - if (v->type == Yosys::AST::AST_BLOCK) - continue; - while (simplify(v, const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } - if (v->type == Yosys::AST::AST_CONSTANT && v->bits_only_01()) { - RTLIL::Const case_item_expr = v->bitsAsConst(width_hint, sign_hint); - RTLIL::Const match = const_eq(case_expr, case_item_expr, sign_hint, sign_hint, 1); - log_assert(match.bits.size() == 1); - if (match.bits.front() == RTLIL::State::S1) { - while (i+1 < GetSize(ast_node->children)) - delete ast_node->children[++i]; - goto keep_const_cond; - } - continue; - } - goto keep_const_cond; - } - if (0) - keep_const_cond: - new_children.push_back(child); - else - delete child; - } - new_children.swap(ast_node->children); - } - } - - dict> backup_memwr_visible; - dict> final_memwr_visible; - - if (ast_node->type == Yosys::AST::AST_CASE && stage == 2) { - backup_memwr_visible = current_memwr_visible; - final_memwr_visible = current_memwr_visible; - } - - // simplify all children first - // (iterate by index as e.g. auto wires can add new children in the process) - for (size_t i = 0; i < ast_node->children.size(); i++) { - bool did_something_here = true; - bool backup_flag_autowire = flag_autowire; - bool backup_unevaluated_tern_branch = unevaluated_tern_branch; - if ((ast_node->type == Yosys::AST::AST_GENFOR || ast_node->type == Yosys::AST::AST_FOR) && i >= 3) - break; - if ((ast_node->type == Yosys::AST::AST_GENIF || ast_node->type == Yosys::AST::AST_GENCASE) && i >= 1) - break; - if (ast_node->type == Yosys::AST::AST_GENBLOCK) - break; - if (ast_node->type == Yosys::AST::AST_CELLARRAY && ast_node->children[i]->type == Yosys::AST::AST_CELL) - continue; - if (ast_node->type == Yosys::AST::AST_BLOCK && !ast_node->str.empty()) - break; - if (ast_node->type == Yosys::AST::AST_PREFIX && i >= 1) - break; - if (ast_node->type == Yosys::AST::AST_DEFPARAM && i == 0) - flag_autowire = true; - if (ast_node->type == Yosys::AST::AST_TERNARY && i > 0 && !unevaluated_tern_branch) { - Yosys::AST::AstNode *chosen = ast_node->get_tern_choice().first; - unevaluated_tern_branch = chosen && chosen != ast_node->children[i]; - } - while (did_something_here && i < ast_node->children.size()) { - bool const_fold_here = const_fold, in_lvalue_here = in_lvalue; - int width_hint_here = width_hint; - bool sign_hint_here = sign_hint; - bool in_param_here = in_param; - if (i == 0 && (ast_node->type == Yosys::AST::AST_REPLICATE || ast_node->type == Yosys::AST::AST_WIRE)) - const_fold_here = true, in_param_here = true; - if (i == 0 && (ast_node->type == Yosys::AST::AST_GENIF || ast_node->type == Yosys::AST::AST_GENCASE)) - in_param_here = true; - if (i == 1 && (ast_node->type == Yosys::AST::AST_FOR || ast_node->type == Yosys::AST::AST_GENFOR)) - in_param_here = true; - if (ast_node->type == Yosys::AST::AST_PARAMETER || ast_node->type == Yosys::AST::AST_LOCALPARAM) - const_fold_here = true; - if (i == 0 && (ast_node->type == Yosys::AST::AST_ASSIGN || ast_node->type == Yosys::AST::AST_ASSIGN_EQ || ast_node->type == Yosys::AST::AST_ASSIGN_LE)) - in_lvalue_here = true; - if (ast_node->type == Yosys::AST::AST_BLOCK) { - current_block = ast_node; - current_block_child = ast_node->children[i]; - } - if ((ast_node->type == Yosys::AST::AST_ALWAYS || ast_node->type == Yosys::AST::AST_INITIAL) && ast_node->children[i]->type == Yosys::AST::AST_BLOCK) - current_top_block = ast_node->children[i]; - if (i == 0 && child_0_is_self_determined) - width_hint_here = -1, sign_hint_here = false; - if (i == 1 && child_1_is_self_determined) - width_hint_here = -1, sign_hint_here = false; - if (i == 2 && child_2_is_self_determined) - width_hint_here = -1, sign_hint_here = false; - if (children_are_self_determined) - width_hint_here = -1, sign_hint_here = false; - did_something_here = simplify(ast_node->children[i], const_fold_here, at_zero, in_lvalue_here, stage, width_hint_here, sign_hint_here, in_param_here); - if (did_something_here) - did_something = true; - } - if (stage == 2 && ast_node->children[i]->type == Yosys::AST::AST_INITIAL && current_ast_mod != ast_node) { - current_ast_mod->children.push_back(ast_node->children[i]); - ast_node->children.erase(ast_node->children.begin() + (i--)); - did_something = true; - } - flag_autowire = backup_flag_autowire; - unevaluated_tern_branch = backup_unevaluated_tern_branch; - if (stage == 2 && ast_node->type == Yosys::AST::AST_CASE) { - for (auto &x : current_memwr_visible) { - for (int y : x.second) - final_memwr_visible[x.first].insert(y); - } - current_memwr_visible = backup_memwr_visible; - } - } - for (auto &attr : ast_node->attributes) { - while (simplify(attr.second, true, false, false, stage, -1, false, true)) - did_something = true; - } - if (ast_node->type == Yosys::AST::AST_CASE && stage == 2) { - current_memwr_visible = final_memwr_visible; - } - if (ast_node->type == Yosys::AST::AST_ALWAYS && stage == 2) { - current_memwr_visible.clear(); - current_memwr_count.clear(); - } - - if (reset_width_after_children) { - width_hint = backup_width_hint; - sign_hint = backup_sign_hint; - if (width_hint < 0) - ast_node->detectSignWidth(width_hint, sign_hint); - } - - current_block = backup_current_block; - current_block_child = backup_current_block_child; - current_top_block = backup_current_top_block; - current_always = backup_current_always; - current_always_clocked = backup_current_always_clocked; - - for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) { - if (it->second == NULL) - current_scope.erase(it->first); - else - current_scope[it->first] = it->second; - } - - Yosys::AST::current_filename = ast_node->filename; - - if (ast_node->type == Yosys::AST::AST_MODULE || ast_node->type == Yosys::AST::AST_INTERFACE) - current_scope.clear(); - - // convert defparam nodes to cell parameters - if (ast_node->type == Yosys::AST::AST_DEFPARAM && !ast_node->children.empty()) - { - if (ast_node->children[0]->type != Yosys::AST::AST_IDENTIFIER) - log_file_error(ast_node->filename, ast_node->location.first_line, "Module name in defparam contains non-constant expressions!\n"); - - string modname, paramname = ast_node->children[0]->str; - - size_t pos = paramname.rfind('.'); - - while (pos != 0 && pos != std::string::npos) - { - modname = paramname.substr(0, pos); - - if (current_scope.count(modname)) - break; - - pos = paramname.rfind('.', pos - 1); - } - - if (pos == std::string::npos) - log_file_error(ast_node->filename, ast_node->location.first_line, "Can't find object for defparam `%s`!\n", RTLIL::unescape_id(paramname).c_str()); - - paramname = "\\" + paramname.substr(pos+1); - - if (current_scope.at(modname)->type != Yosys::AST::AST_CELL) - log_file_error(ast_node->filename, ast_node->location.first_line, "Defparam argument `%s . %s` does not match a cell!\n", - RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paramname).c_str()); - - Yosys::AST::AstNode *paraset = new Yosys::AST::AstNode(Yosys::AST::AST_PARASET, ast_node->children[1]->clone(), GetSize(ast_node->children) > 2 ? ast_node->children[2]->clone() : NULL); - paraset->str = paramname; - - Yosys::AST::AstNode *cell = current_scope.at(modname); - cell->children.insert(cell->children.begin() + 1, paraset); - ast_node->delete_children(); - } - - // resolve typedefs - if (ast_node->type == Yosys::AST::AST_TYPEDEF) { - log_assert(ast_node->children.size() == 1); - auto type_node = ast_node->children[0]; - log_assert(type_node->type == Yosys::AST::AST_WIRE || type_node->type == Yosys::AST::AST_MEMORY || type_node->type == Yosys::AST::AST_STRUCT || type_node->type == Yosys::AST::AST_UNION); - while (simplify(type_node, const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { - did_something = true; - } - log_assert(!type_node->is_custom_type); - } - - // resolve types of wires - if (ast_node->type == Yosys::AST::AST_WIRE || ast_node->type == Yosys::AST::AST_MEMORY || ast_node->type == Yosys::AST::AST_STRUCT_ITEM) { - if (ast_node->is_custom_type) { - log_assert(ast_node->children.size() >= 1); - log_assert(ast_node->children[0]->type == Yosys::AST::AST_WIRETYPE); - auto type_name = ast_node->children[0]->str; - if (!current_scope.count(type_name)) { - log_file_error(ast_node->filename, ast_node->location.first_line, "Unknown identifier `%s' used as type name\n", type_name.c_str()); - } - Yosys::AST::AstNode *resolved_type_node = current_scope.at(type_name); - if (resolved_type_node->type != Yosys::AST::AST_TYPEDEF) - log_file_error(ast_node->filename, ast_node->location.first_line, "`%s' does not name a type\n", type_name.c_str()); - log_assert(resolved_type_node->children.size() == 1); - Yosys::AST::AstNode *template_node = resolved_type_node->children[0]; - - // Ensure typedef itself is fully simplified - while (simplify(template_node, const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; - - if (template_node->type == Yosys::AST::AST_STRUCT || template_node->type == Yosys::AST::AST_UNION) { - // replace with wire representing the packed structure - newNode = make_packed_struct(template_node, ast_node->str); - newNode->attributes[ID::wiretype] = ast_node->mkconst_str(resolved_type_node->str); - // add original input/output attribute to resolved wire - newNode->is_input = ast_node->is_input; - newNode->is_output = ast_node->is_output; - current_scope[ast_node->str] = ast_node; - goto apply_newNode; - } - - // Remove type reference - delete ast_node->children[0]; - ast_node->children.erase(ast_node->children.begin()); - - if (ast_node->type == Yosys::AST::AST_WIRE) - ast_node->type = template_node->type; - ast_node->is_reg = template_node->is_reg; - ast_node->is_logic = template_node->is_logic; - ast_node->is_signed = template_node->is_signed; - ast_node->is_string = template_node->is_string; - ast_node->is_custom_type = template_node->is_custom_type; - - ast_node->range_valid = template_node->range_valid; - ast_node->range_swapped = template_node->range_swapped; - ast_node->range_left = template_node->range_left; - ast_node->range_right = template_node->range_right; - - ast_node->attributes[ID::wiretype] = ast_node->mkconst_str(resolved_type_node->str); - - // if an enum then add attributes to support simulator tracing - annotateTypedEnums(ast_node, template_node); - - // Insert clones children from template at beginning - for (int i = 0; i < GetSize(template_node->children); i++) - ast_node->children.insert(ast_node->children.begin() + i, template_node->children[i]->clone()); - - if (ast_node->type == Yosys::AST::AST_MEMORY && GetSize(ast_node->children) == 1) { - // Single-bit memories must have [0:0] range - Yosys::AST::AstNode *rng = make_range(0, 0); - ast_node->children.insert(ast_node->children.begin(), rng); - } - did_something = true; - } - log_assert(!ast_node->is_custom_type); - } - - // resolve types of parameters - if (ast_node->type == Yosys::AST::AST_LOCALPARAM || ast_node->type == Yosys::AST::AST_PARAMETER) { - if (ast_node->is_custom_type) { - log_assert(ast_node->children.size() == 2); - log_assert(ast_node->children[1]->type == Yosys::AST::AST_WIRETYPE); - auto type_name = ast_node->children[1]->str; - if (!current_scope.count(type_name)) { - log_file_error(ast_node->filename, ast_node->location.first_line, "Unknown identifier `%s' used as type name\n", type_name.c_str()); - } - Yosys::AST::AstNode *resolved_type_node = current_scope.at(type_name); - if (resolved_type_node->type != Yosys::AST::AST_TYPEDEF) - log_file_error(ast_node->filename, ast_node->location.first_line, "`%s' does not name a type\n", type_name.c_str()); - log_assert(resolved_type_node->children.size() == 1); - Yosys::AST::AstNode *template_node = resolved_type_node->children[0]; - - // Ensure typedef itself is fully simplified - while (simplify(template_node, const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; - - if (template_node->type == Yosys::AST::AST_STRUCT || template_node->type == Yosys::AST::AST_UNION) { - // replace with wire representing the packed structure - newNode = make_packed_struct(template_node, ast_node->str); - newNode->attributes[ID::wiretype] = ast_node->mkconst_str(resolved_type_node->str); - newNode->type = ast_node->type; - current_scope[ast_node->str] = ast_node; - // copy param value, it needs to be 1st value - delete ast_node->children[1]; - ast_node->children.pop_back(); - newNode->children.insert(newNode->children.begin(), ast_node->children[0]->clone()); - goto apply_newNode; - } - delete ast_node->children[1]; - ast_node->children.pop_back(); - - if (template_node->type == Yosys::AST::AST_MEMORY) - log_file_error(ast_node->filename, ast_node->location.first_line, "unpacked array type `%s' cannot be used for a parameter\n", ast_node->children[1]->str.c_str()); - ast_node->is_signed = template_node->is_signed; - ast_node->is_string = template_node->is_string; - ast_node->is_custom_type = template_node->is_custom_type; - - ast_node->range_valid = template_node->range_valid; - ast_node->range_swapped = template_node->range_swapped; - ast_node->range_left = template_node->range_left; - ast_node->range_right = template_node->range_right; - ast_node->attributes[ID::wiretype] = ast_node->mkconst_str(resolved_type_node->str); - for (auto template_child : template_node->children) - ast_node->children.push_back(template_child->clone()); - did_something = true; - } - log_assert(!ast_node->is_custom_type); - } - - // resolve constant prefixes - if (ast_node->type == Yosys::AST::AST_PREFIX) { - if (ast_node->children[0]->type != Yosys::AST::AST_CONSTANT) { - // dumpAst(NULL, "> "); - log_file_error(ast_node->filename, ast_node->location.first_line, "Index in generate block prefix syntax is not constant!\n"); - } - if (ast_node->children[1]->type == Yosys::AST::AST_PREFIX) - simplify(ast_node->children[1], const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param); - log_assert(ast_node->children[1]->type == Yosys::AST::AST_IDENTIFIER); - newNode = ast_node->children[1]->clone(); - const char *second_part = ast_node->children[1]->str.c_str(); - if (second_part[0] == '\\') - second_part++; - newNode->str = stringf("%s[%d].%s", ast_node->str.c_str(), ast_node->children[0]->integer, second_part); - goto apply_newNode; - } - - // evaluate TO_BITS nodes - if (ast_node->type == Yosys::AST::AST_TO_BITS) { - if (ast_node->children[0]->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Left operand of to_bits expression is not constant!\n"); - if (ast_node->children[1]->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Right operand of to_bits expression is not constant!\n"); - RTLIL::Const new_value = ast_node->children[1]->bitsAsConst(ast_node->children[0]->bitsAsConst().as_int(), ast_node->children[1]->is_signed); - newNode = Yosys::AST::AstNode::mkconst_bits(new_value.bits, ast_node->children[1]->is_signed); - goto apply_newNode; - } - - // annotate constant ranges - if (ast_node->type == Yosys::AST::AST_RANGE) { - bool old_range_valid = ast_node->range_valid; - ast_node->range_valid = false; - ast_node->range_swapped = false; - ast_node->range_left = -1; - ast_node->range_right = 0; - log_assert(ast_node->children.size() >= 1); - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT) { - ast_node->range_valid = true; - ast_node->range_left = ast_node->children[0]->integer; - if (ast_node->children.size() == 1) - ast_node->range_right = ast_node->range_left; - } - if (ast_node->children.size() >= 2) { - if (ast_node->children[1]->type == Yosys::AST::AST_CONSTANT) - ast_node->range_right = ast_node->children[1]->integer; - else - ast_node->range_valid = false; - } - if (old_range_valid != ast_node->range_valid) - did_something = true; - if (ast_node->range_valid && ast_node->range_right > ast_node->range_left) { - int tmp = ast_node->range_right; - ast_node->range_right = ast_node->range_left; - ast_node->range_left = tmp; - ast_node->range_swapped = true; - } - } - - // annotate wires with their ranges - if (ast_node->type == Yosys::AST::AST_WIRE) { - if (ast_node->children.size() > 0) { - if (ast_node->children[0]->range_valid) { - if (!ast_node->range_valid) - did_something = true; - ast_node->range_valid = true; - ast_node->range_swapped = ast_node->children[0]->range_swapped; - ast_node->range_left = ast_node->children[0]->range_left; - ast_node->range_right = ast_node->children[0]->range_right; - bool force_upto = false, force_downto = false; - if (ast_node->attributes.count(ID::force_upto)) { - Yosys::AST::AstNode *val = ast_node->attributes[ID::force_upto]; - if (val->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Attribute `force_upto' with non-constant value!\n"); - force_upto = val->asAttrConst().as_bool(); - } - if (ast_node->attributes.count(ID::force_downto)) { - Yosys::AST::AstNode *val = ast_node->attributes[ID::force_downto]; - if (val->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Attribute `force_downto' with non-constant value!\n"); - force_downto = val->asAttrConst().as_bool(); - } - if (force_upto && force_downto) - log_file_error(ast_node->filename, ast_node->location.first_line, "Attributes `force_downto' and `force_upto' cannot be both set!\n"); - if ((force_upto && !ast_node->range_swapped) || (force_downto && ast_node->range_swapped)) { - std::swap(ast_node->range_left, ast_node->range_right); - ast_node->range_swapped = force_upto; - } - } - } else { - if (!ast_node->range_valid) - did_something = true; - ast_node->range_valid = true; - ast_node->range_swapped = false; - ast_node->range_left = 0; - ast_node->range_right = 0; - } - } - - // resolve multiranges on memory decl - if (ast_node->type == Yosys::AST::AST_MEMORY && ast_node->children.size() > 1 && ast_node->children[1]->type == Yosys::AST::AST_MULTIRANGE) - { - int total_size = 1; - ast_node->multirange_dimensions.clear(); - ast_node->multirange_swapped.clear(); - for (auto range : ast_node->children[1]->children) { - if (!range->range_valid) - log_file_error(ast_node->filename, ast_node->location.first_line, "Non-constant range on memory decl.\n"); - ast_node->multirange_dimensions.push_back(min(range->range_left, range->range_right)); - ast_node->multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1); - ast_node->multirange_swapped.push_back(range->range_swapped); - total_size *= ast_node->multirange_dimensions.back(); - } - delete ast_node->children[1]; - ast_node->children[1] = new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, ast_node->mkconst_int(0, true), ast_node->mkconst_int(total_size-1, true)); - did_something = true; - } - - // resolve multiranges on memory access - if (ast_node->type == Yosys::AST::AST_IDENTIFIER && ast_node->id2ast && ast_node->id2ast->type == Yosys::AST::AST_MEMORY && ast_node->children.size() > 0 && ast_node->children[0]->type == Yosys::AST::AST_MULTIRANGE) - { - Yosys::AST::AstNode *index_expr = nullptr; - - ast_node->integer = ast_node->children[0]->children.size(); // save original number of dimensions for $size() etc. - for (int i = 0; 2*i < GetSize(ast_node->id2ast->multirange_dimensions); i++) - { - if (GetSize(ast_node->children[0]->children) <= i) - log_file_error(ast_node->filename, ast_node->location.first_line, "Insufficient number of array indices for %s.\n", log_id(ast_node->str)); - - Yosys::AST::AstNode *new_index_expr = ast_node->children[0]->children[i]->children.at(0)->clone(); - - if (ast_node->id2ast->multirange_dimensions[2*i]) - new_index_expr = new Yosys::AST::AstNode(Yosys::AST::AST_SUB, new_index_expr, ast_node->mkconst_int(ast_node->id2ast->multirange_dimensions[2*i], true)); - - if (i == 0) - index_expr = new_index_expr; - else - index_expr = new Yosys::AST::AstNode(Yosys::AST::AST_ADD, new Yosys::AST::AstNode(Yosys::AST::AST_MUL, index_expr, ast_node->mkconst_int(ast_node->id2ast->multirange_dimensions[2*i+1], true)), new_index_expr); - } - - for (int i = GetSize(ast_node->id2ast->multirange_dimensions)/2; i < GetSize(ast_node->children[0]->children); i++) - ast_node->children.push_back(ast_node->children[0]->children[i]->clone()); - - delete ast_node->children[0]; - if (index_expr == nullptr) - ast_node->children.erase(ast_node->children.begin()); - else - ast_node->children[0] = new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, index_expr); - - did_something = true; - } - - // trim/extend parameters - if (ast_node->type == Yosys::AST::AST_PARAMETER || ast_node->type == Yosys::AST::AST_LOCALPARAM || ast_node->type == Yosys::AST::AST_ENUM_ITEM) { - if (ast_node->children.size() > 1 && ast_node->children[1]->type == Yosys::AST::AST_RANGE) { - if (!ast_node->children[1]->range_valid) - log_file_error(ast_node->filename, ast_node->location.first_line, "Non-constant width range on parameter decl.\n"); - int width = std::abs(ast_node->children[1]->range_left - ast_node->children[1]->range_right) + 1; - if (ast_node->children[0]->type == Yosys::AST::AST_REALVALUE) { - RTLIL::Const constvalue = ast_node->children[0]->realAsConst(width); - log_file_warning(ast_node->filename, ast_node->location.first_line, "converting real value %e to binary %s.\n", - ast_node->children[0]->realvalue, log_signal(constvalue)); - delete ast_node->children[0]; - ast_node->children[0] = Yosys::AST::AstNode::mkconst_bits(constvalue.bits, sign_hint); - did_something = true; - } - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT) { - if (width != int(ast_node->children[0]->bits.size())) { - RTLIL::SigSpec sig(ast_node->children[0]->bits); - sig.extend_u0(width, ast_node->children[0]->is_signed); - Yosys::AST::AstNode *old_child_0 = ast_node->children[0]; - ast_node->children[0] = Yosys::AST::AstNode::mkconst_bits(sig.as_const().bits, ast_node->is_signed); - delete old_child_0; - } - ast_node->children[0]->is_signed = ast_node->is_signed; - } - ast_node->range_valid = true; - ast_node->range_swapped = ast_node->children[1]->range_swapped; - ast_node->range_left = ast_node->children[1]->range_left; - ast_node->range_right = ast_node->children[1]->range_right; - } else - if (ast_node->children.size() > 1 && ast_node->children[1]->type == Yosys::AST::AST_REALVALUE && ast_node->children[0]->type == Yosys::AST::AST_CONSTANT) { - double as_realvalue = ast_node->children[0]->asReal(sign_hint); - delete ast_node->children[0]; - ast_node->children[0] = new Yosys::AST::AstNode(Yosys::AST::AST_REALVALUE); - ast_node->children[0]->realvalue = as_realvalue; - did_something = true; - } - } - - if (ast_node->type == Yosys::AST::AST_IDENTIFIER && !ast_node->basic_prep) { - // check if a plausible struct member sss.mmmm - std::string sname; - if (name_has_dot(ast_node->str, sname)) { - if (current_scope.count(ast_node->str) > 0) { - auto item_node = current_scope[ast_node->str]; - if (item_node->type == Yosys::AST::AST_STRUCT_ITEM || item_node->type == Yosys::AST::AST_STRUCT || item_node->type == Yosys::AST::AST_UNION) { - // structure member, rewrite ast_node node to reference the packed struct wire - auto range = Yosys::AST::make_struct_member_range(ast_node, item_node); - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER, range); - newNode->str = sname; - // save type and original number of dimensions for $size() etc. - newNode->attributes[ID::wiretype] = item_node->clone(); - if (!item_node->multirange_dimensions.empty() && ast_node->children.size() > 0) { - if (ast_node->children[0]->type == Yosys::AST::AST_RANGE) - newNode->integer = 1; - else if (ast_node->children[0]->type == Yosys::AST::AST_MULTIRANGE) - newNode->integer = ast_node->children[0]->children.size(); - } - newNode->basic_prep = true; - if (item_node->is_signed) - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_TO_SIGNED, newNode); - goto apply_newNode; - } - } - } - } - // annotate identifiers using scope resolution and create auto-wires as needed - if (ast_node->type == Yosys::AST::AST_IDENTIFIER) { - if (current_scope.count(ast_node->str) == 0) { - Yosys::AST::AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod; - ast_node->str = ast_node->try_pop_module_prefix(); - for (auto node : current_scope_ast->children) { - //log("looking at mod scope child %s\n", type2str(node->type).c_str()); - switch (node->type) { - case Yosys::AST::AST_PARAMETER: - case Yosys::AST::AST_LOCALPARAM: - case Yosys::AST::AST_WIRE: - case Yosys::AST::AST_AUTOWIRE: - case Yosys::AST::AST_GENVAR: - case Yosys::AST::AST_MEMORY: - case Yosys::AST::AST_FUNCTION: - case Yosys::AST::AST_TASK: - case Yosys::AST::AST_DPI_FUNCTION: - //log("found child %s, %s\n", type2str(node->type).c_str(), node->str.c_str()); - if (ast_node->str == node->str) { - //log("add %s, type %s to scope\n", ast_node->str.c_str(), type2str(node->type).c_str()); - current_scope[node->str] = node; - } - break; - case Yosys::AST::AST_ENUM: - current_scope[node->str] = node; - for (auto enum_node : node->children) { - log_assert(enum_node->type==Yosys::AST::AST_ENUM_ITEM); - if (ast_node->str == enum_node->str) { - //log("\nadding enum item %s to scope\n", ast_node->str.c_str()); - current_scope[ast_node->str] = enum_node; - } - } - break; - default: - break; - } - } - } - if (current_scope.count(ast_node->str) == 0) { - if (current_ast_mod == nullptr) { - log_file_error(ast_node->filename, ast_node->location.first_line, "Identifier `%s' is implicitly declared outside of a module.\n", ast_node->str.c_str()); - } else if (flag_autowire || ast_node->str == "\\$global_clock") { - Yosys::AST::AstNode *auto_wire = new Yosys::AST::AstNode(Yosys::AST::AST_AUTOWIRE); - auto_wire->str = ast_node->str; - current_ast_mod->children.push_back(auto_wire); - current_scope[ast_node->str] = auto_wire; - did_something = true; - } else { - log_file_error(ast_node->filename, ast_node->location.first_line, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", ast_node->str.c_str()); - } - } - if (ast_node->id2ast != current_scope[ast_node->str]) { - ast_node->id2ast = current_scope[ast_node->str]; - did_something = true; - } - } - - // split memory access with bit select to individual statements - if (ast_node->type == Yosys::AST::AST_IDENTIFIER && ast_node->children.size() == 2 && ast_node->children[0]->type == Yosys::AST::AST_RANGE && ast_node->children[1]->type == Yosys::AST::AST_RANGE && !in_lvalue && stage == 2) - { - if (ast_node->id2ast == NULL || ast_node->id2ast->type != Yosys::AST::AST_MEMORY || ast_node->children[0]->children.size() != 1) - log_file_error(ast_node->filename, ast_node->location.first_line, "Invalid bit-select on memory access!\n"); - - int mem_width, mem_size, addr_bits; - ast_node->id2ast->meminfo(mem_width, mem_size, addr_bits); - - int data_range_left = ast_node->id2ast->children[0]->range_left; - int data_range_right = ast_node->id2ast->children[0]->range_right; - - if (ast_node->id2ast->children[0]->range_swapped) - std::swap(data_range_left, data_range_right); - - std::stringstream sstr; - sstr << "$mem2bits$" << ast_node->str << "$" << encode_filename(ast_node->filename) << ":" << ast_node->location.first_line << "$" << (autoidx++); - std::string wire_id = sstr.str(); - - Yosys::AST::AstNode *wire = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE, new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, ast_node->mkconst_int(data_range_left, true), ast_node->mkconst_int(data_range_right, true))); - wire->str = wire_id; - if (current_block) - wire->attributes[ID::nosync] = ast_node->mkconst_int(1, false); - current_ast_mod->children.push_back(wire); - while (simplify(wire, true, false, false, 1, -1, false, false)) { } - - Yosys::AST::AstNode *data = ast_node->clone(); - delete data->children[1]; - data->children.pop_back(); - - Yosys::AST::AstNode *assign = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), data); - assign->children[0]->str = wire_id; - assign->children[0]->was_checked = true; - - if (current_block) - { - size_t assign_idx = 0; - while (assign_idx < current_block->children.size() && current_block->children[assign_idx] != current_block_child) - assign_idx++; - log_assert(assign_idx < current_block->children.size()); - current_block->children.insert(current_block->children.begin()+assign_idx, assign); - wire->is_reg = true; - } - else - { - Yosys::AST::AstNode *proc = new Yosys::AST::AstNode(Yosys::AST::AST_ALWAYS, new Yosys::AST::AstNode(Yosys::AST::AST_BLOCK)); - proc->children[0]->children.push_back(assign); - current_ast_mod->children.push_back(proc); - } - - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER, ast_node->children[1]->clone()); - newNode->str = wire_id; - newNode->integer = ast_node->integer; // save original number of dimensions for $size() etc. - newNode->id2ast = wire; - goto apply_newNode; - } - - if (ast_node->type == Yosys::AST::AST_WHILE) - log_file_error(ast_node->filename, ast_node->location.first_line, "While loops are only allowed in constant functions!\n"); - - if (ast_node->type == Yosys::AST::AST_REPEAT) - { - Yosys::AST::AstNode *count = ast_node->children[0]; - Yosys::AST::AstNode *body = ast_node->children[1]; - - // eval count expression - while (simplify(count, true, false, false, stage, 32, true, false)) { } - - if (count->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Repeat loops outside must have constant repeat counts!\n"); - - // convert to a block with the body repeated n times - ast_node->type = Yosys::AST::AST_BLOCK; - ast_node->children.clear(); - for (int i = 0; i < count->bitsAsConst().as_int(); i++) - ast_node->children.insert(ast_node->children.begin(), body->clone()); - - delete count; - delete body; - did_something = true; - } - - // unroll for loops and generate-for blocks - if ((ast_node->type == Yosys::AST::AST_GENFOR || ast_node->type == Yosys::AST::AST_FOR) && ast_node->children.size() != 0) - { - Yosys::AST::AstNode *init_ast = ast_node->children[0]; - Yosys::AST::AstNode *while_ast = ast_node->children[1]; - Yosys::AST::AstNode *next_ast = ast_node->children[2]; - Yosys::AST::AstNode *body_ast = ast_node->children[3]; - - while (body_ast->type == Yosys::AST::AST_GENBLOCK && body_ast->str.empty() && - body_ast->children.size() == 1 && body_ast->children.at(0)->type == Yosys::AST::AST_GENBLOCK) - body_ast = body_ast->children.at(0); - - const char* loop_type_str = "procedural"; - const char* var_type_str = "register"; - Yosys::AST::AstNodeType var_type = Yosys::AST::AST_WIRE; - if (ast_node->type == Yosys::AST::AST_GENFOR) { - loop_type_str = "generate"; - var_type_str = "genvar"; - var_type = Yosys::AST::AST_GENVAR; - } - - if (init_ast->type != Yosys::AST::AST_ASSIGN_EQ) - log_file_error(ast_node->filename, ast_node->location.first_line, "Unsupported 1st expression of %s for-loop!\n", loop_type_str); - if (next_ast->type != Yosys::AST::AST_ASSIGN_EQ) - log_file_error(ast_node->filename, ast_node->location.first_line, "Unsupported 3rd expression of %s for-loop!\n", loop_type_str); - - if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != var_type) - log_file_error(ast_node->filename, ast_node->location.first_line, "Left hand side of 1st expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); - if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != var_type) - log_file_error(ast_node->filename, ast_node->location.first_line, "Left hand side of 3rd expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); - - if (init_ast->children[0]->id2ast != next_ast->children[0]->id2ast) - log_file_error(ast_node->filename, ast_node->location.first_line, "Incompatible left-hand sides in 1st and 3rd expression of %s for-loop!\n", loop_type_str); - - // eval 1st expression - Yosys::AST::AstNode *varbuf = init_ast->children[1]->clone(); - { - int expr_width_hint = -1; - bool expr_sign_hint = true; - varbuf->detectSignWidth(expr_width_hint, expr_sign_hint); - while (simplify(varbuf, true, false, false, stage, 32, true, false)) { } - } - - if (varbuf->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Right hand side of 1st expression of %s for-loop is not constant!\n", loop_type_str); - - auto resolved = current_scope.at(init_ast->children[0]->str); - if (resolved->range_valid) { - int const_size = varbuf->range_left - varbuf->range_right; - int resolved_size = resolved->range_left - resolved->range_right; - if (const_size < resolved_size) { - for (int i = const_size; i < resolved_size; i++) - varbuf->bits.push_back(resolved->is_signed ? varbuf->bits.back() : State::S0); - varbuf->range_left = resolved->range_left; - varbuf->range_right = resolved->range_right; - varbuf->range_swapped = resolved->range_swapped; - varbuf->range_valid = resolved->range_valid; - } - } - - varbuf = new Yosys::AST::AstNode(Yosys::AST::AST_LOCALPARAM, varbuf); - varbuf->str = init_ast->children[0]->str; - - Yosys::AST::AstNode *backup_scope_varbuf = current_scope[varbuf->str]; - current_scope[varbuf->str] = varbuf; - - size_t current_block_idx = 0; - if (ast_node->type == Yosys::AST::AST_FOR) { - while (current_block_idx < current_block->children.size() && - current_block->children[current_block_idx] != current_block_child) - current_block_idx++; - } - - while (1) - { - // eval 2nd expression - Yosys::AST::AstNode *buf = while_ast->clone(); - { - int expr_width_hint = -1; - bool expr_sign_hint = true; - buf->detectSignWidth(expr_width_hint, expr_sign_hint); - while (simplify(buf, true, false, false, stage, expr_width_hint, expr_sign_hint, false)) { } - } - - if (buf->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "2nd expression of %s for-loop is not constant!\n", loop_type_str); - - if (buf->integer == 0) { - delete buf; - break; - } - delete buf; - - // expand body - int index = varbuf->children[0]->integer; - log_assert(body_ast->type == Yosys::AST::AST_GENBLOCK || body_ast->type == Yosys::AST::AST_BLOCK); - log_assert(!body_ast->str.empty()); - buf = body_ast->clone(); - - std::stringstream sstr; - sstr << buf->str << "[" << index << "]."; - std::string prefix = sstr.str(); - - // create a scoped localparam for the current value of the loop variable - Yosys::AST::AstNode *local_index = varbuf->clone(); - size_t pos = local_index->str.rfind('.'); - if (pos != std::string::npos) // remove outer prefix - local_index->str = "\\" + local_index->str.substr(pos + 1); - local_index->str = prefix_id(prefix, local_index->str); - current_scope[local_index->str] = local_index; - current_ast_mod->children.push_back(local_index); - - buf->expand_genblock(prefix); - - if (ast_node->type == Yosys::AST::AST_GENFOR) { - for (size_t i = 0; i < buf->children.size(); i++) { - simplify(buf->children[i], const_fold, false, false, stage, -1, false, false); - current_ast_mod->children.push_back(buf->children[i]); - } - } else { - for (size_t i = 0; i < buf->children.size(); i++) - current_block->children.insert(current_block->children.begin() + current_block_idx++, buf->children[i]); - } - buf->children.clear(); - delete buf; - - // eval 3rd expression - buf = next_ast->children[1]->clone(); - { - int expr_width_hint = -1; - bool expr_sign_hint = true; - buf->detectSignWidth(expr_width_hint, expr_sign_hint); - while (simplify(buf, true, false, false, stage, expr_width_hint, expr_sign_hint, true)) { } - } - - if (buf->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Right hand side of 3rd expression of %s for-loop is not constant (%s)!\n", loop_type_str, type2str(buf->type).c_str()); - - delete varbuf->children[0]; - varbuf->children[0] = buf; - } - - if (ast_node->type == Yosys::AST::AST_FOR) { - Yosys::AST::AstNode *buf = next_ast->clone(); - delete buf->children[1]; - buf->children[1] = varbuf->children[0]->clone(); - current_block->children.insert(current_block->children.begin() + current_block_idx++, buf); - } - - current_scope[varbuf->str] = backup_scope_varbuf; - delete varbuf; - ast_node->delete_children(); - did_something = true; - } - - // check for local objects in unnamed block - if (ast_node->type == Yosys::AST::AST_BLOCK && ast_node->str.empty()) - { - for (size_t i = 0; i < ast_node->children.size(); i++) - if (ast_node->children[i]->type == Yosys::AST::AST_WIRE || ast_node->children[i]->type == Yosys::AST::AST_MEMORY || ast_node->children[i]->type == Yosys::AST::AST_PARAMETER || ast_node->children[i]->type == Yosys::AST::AST_LOCALPARAM || ast_node->children[i]->type == Yosys::AST::AST_TYPEDEF) - { - log_assert(!VERILOG_FRONTEND::sv_mode); - log_file_error(ast_node->children[i]->filename, ast_node->children[i]->location.first_line, "Local declaration in unnamed block is only supported in SystemVerilog mode!\n"); - } - } - - // transform block with name - if (ast_node->type == Yosys::AST::AST_BLOCK && !ast_node->str.empty()) - { - ast_node->expand_genblock(ast_node->str + "."); - - // if ast_node is an autonamed block is in an always_comb - if (current_always && current_always->attributes.count(ID::always_comb) - && is_autonamed_block(ast_node->str)) - // track local variables in ast_node block so we can consider adding - // nosync once the block has been fully elaborated - for (Yosys::AST::AstNode *child : ast_node->children) - if (child->type == Yosys::AST::AST_WIRE && - !child->attributes.count(ID::nosync)) - mark_auto_nosync(ast_node, child); - - std::vector new_children; - for (size_t i = 0; i < ast_node->children.size(); i++) - if (ast_node->children[i]->type == Yosys::AST::AST_WIRE || ast_node->children[i]->type == Yosys::AST::AST_MEMORY || ast_node->children[i]->type == Yosys::AST::AST_PARAMETER || ast_node->children[i]->type == Yosys::AST::AST_LOCALPARAM || ast_node->children[i]->type == Yosys::AST::AST_TYPEDEF) { - simplify(ast_node->children[i], false, false, false, stage, -1, false, false); - current_ast_mod->children.push_back(ast_node->children[i]); - current_scope[ast_node->children[i]->str] = ast_node->children[i]; - } else - new_children.push_back(ast_node->children[i]); - - ast_node->children.swap(new_children); - did_something = true; - ast_node->str.clear(); - } - - // simplify unconditional generate block - if (ast_node->type == Yosys::AST::AST_GENBLOCK && ast_node->children.size() != 0) - { - if (!ast_node->str.empty()) { - ast_node->expand_genblock(ast_node->str + "."); - } - - for (size_t i = 0; i < ast_node->children.size(); i++) { - simplify(ast_node->children[i], const_fold, false, false, stage, -1, false, false); - current_ast_mod->children.push_back(ast_node->children[i]); - } - - ast_node->children.clear(); - did_something = true; - } - - // simplify generate-if blocks - if (ast_node->type == Yosys::AST::AST_GENIF && ast_node->children.size() != 0) - { - Yosys::AST::AstNode *buf = ast_node->children[0]->clone(); - while (simplify(buf, true, false, false, stage, width_hint, sign_hint, false)) { } - if (buf->type != Yosys::AST::AST_CONSTANT) { - // for (auto f : log_files) - // dumpAst(f, "verilog-ast> "); - log_file_error(ast_node->filename, ast_node->location.first_line, "Condition for generate if is not constant!\n"); - } - if (buf->asBool() != 0) { - delete buf; - buf = ast_node->children[1]->clone(); - } else { - delete buf; - buf = ast_node->children.size() > 2 ? ast_node->children[2]->clone() : NULL; - } - - if (buf) - { - if (buf->type != Yosys::AST::AST_GENBLOCK) - buf = new Yosys::AST::AstNode(Yosys::AST::AST_GENBLOCK, buf); - - if (!buf->str.empty()) { - buf->expand_genblock(buf->str + "."); - } - - for (size_t i = 0; i < buf->children.size(); i++) { - simplify(buf->children[i], const_fold, false, false, stage, -1, false, false); - current_ast_mod->children.push_back(buf->children[i]); - } - - buf->children.clear(); - delete buf; - } - - ast_node->delete_children(); - did_something = true; - } - - // simplify generate-case blocks - if (ast_node->type == Yosys::AST::AST_GENCASE && ast_node->children.size() != 0) - { - Yosys::AST::AstNode *buf = ast_node->children[0]->clone(); - while (simplify(buf, true, false, false, stage, width_hint, sign_hint, false)) { } - if (buf->type != Yosys::AST::AST_CONSTANT) { - // for (auto f : log_files) - // dumpAst(f, "verilog-ast> "); - log_file_error(ast_node->filename, ast_node->location.first_line, "Condition for generate case is not constant!\n"); - } - - bool ref_signed = buf->is_signed; - RTLIL::Const ref_value = buf->bitsAsConst(); - delete buf; - - Yosys::AST::AstNode *selected_case = NULL; - for (size_t i = 1; i < ast_node->children.size(); i++) - { - log_assert(ast_node->children.at(i)->type == Yosys::AST::AST_COND || ast_node->children.at(i)->type == Yosys::AST::AST_CONDX || ast_node->children.at(i)->type == Yosys::AST::AST_CONDZ); - - Yosys::AST::AstNode *this_genblock = NULL; - for (auto child : ast_node->children.at(i)->children) { - log_assert(this_genblock == NULL); - if (child->type == Yosys::AST::AST_GENBLOCK) - this_genblock = child; - } - - for (auto child : ast_node->children.at(i)->children) - { - if (child->type == Yosys::AST::AST_DEFAULT) { - if (selected_case == NULL) - selected_case = this_genblock; - continue; - } - if (child->type == Yosys::AST::AST_GENBLOCK) - continue; - - buf = child->clone(); - while (simplify(buf, true, false, false, stage, width_hint, sign_hint, true)) { } - if (buf->type != Yosys::AST::AST_CONSTANT) { - // for (auto f : log_files) - // dumpAst(f, "verilog-ast> "); - log_file_error(ast_node->filename, ast_node->location.first_line, "Expression in generate case is not constant!\n"); - } - - bool is_selected = RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool(); - delete buf; - - if (is_selected) { - selected_case = this_genblock; - i = ast_node->children.size(); - break; - } - } - } - - if (selected_case != NULL) - { - log_assert(selected_case->type == Yosys::AST::AST_GENBLOCK); - buf = selected_case->clone(); - - if (!buf->str.empty()) { - buf->expand_genblock(buf->str + "."); - } - - for (size_t i = 0; i < buf->children.size(); i++) { - simplify(buf->children[i], const_fold, false, false, stage, -1, false, false); - current_ast_mod->children.push_back(buf->children[i]); - } - - buf->children.clear(); - delete buf; - } - - ast_node->delete_children(); - did_something = true; - } - - // unroll cell arrays - if (ast_node->type == Yosys::AST::AST_CELLARRAY) - { - if (!ast_node->children.at(0)->range_valid) - log_file_error(ast_node->filename, ast_node->location.first_line, "Non-constant array range on cell array.\n"); - - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_GENBLOCK); - int num = max(ast_node->children.at(0)->range_left, ast_node->children.at(0)->range_right) - min(ast_node->children.at(0)->range_left, ast_node->children.at(0)->range_right) + 1; - - for (int i = 0; i < num; i++) { - int idx = ast_node->children.at(0)->range_left > ast_node->children.at(0)->range_right ? ast_node->children.at(0)->range_right + i : ast_node->children.at(0)->range_right - i; - Yosys::AST::AstNode *new_cell = ast_node->children.at(1)->clone(); - newNode->children.push_back(new_cell); - new_cell->str += stringf("[%d]", idx); - if (new_cell->type == Yosys::AST::AST_PRIMITIVE) { - log_file_error(ast_node->filename, ast_node->location.first_line, "Cell arrays of primitives are currently not supported.\n"); - } else { - log_assert(new_cell->children.at(0)->type == Yosys::AST::AST_CELLTYPE); - new_cell->children.at(0)->str = stringf("$array:%d:%d:%s", i, num, new_cell->children.at(0)->str.c_str()); - } - } - - goto apply_newNode; - } - - // replace primitives with assignments - if (ast_node->type == Yosys::AST::AST_PRIMITIVE) - { - if (ast_node->children.size() < 2) - log_file_error(ast_node->filename, ast_node->location.first_line, "Insufficient number of arguments for primitive `%s'!\n", ast_node->str.c_str()); - - std::vector children_list; - for (auto child : ast_node->children) { - log_assert(child->type == Yosys::AST::AST_ARGUMENT); - log_assert(child->children.size() == 1); - children_list.push_back(child->children[0]); - child->children.clear(); - delete child; - } - ast_node->children.clear(); - - if (ast_node->str == "bufif0" || ast_node->str == "bufif1" || ast_node->str == "notif0" || ast_node->str == "notif1") - { - if (children_list.size() != 3) - log_file_error(ast_node->filename, ast_node->location.first_line, "Invalid number of arguments for primitive `%s'!\n", ast_node->str.c_str()); - - std::vector z_const(1, RTLIL::State::Sz); - - Yosys::AST::AstNode *mux_input = children_list.at(1); - if (ast_node->str == "notif0" || ast_node->str == "notif1") { - mux_input = new Yosys::AST::AstNode(Yosys::AST::AST_BIT_NOT, mux_input); - } - Yosys::AST::AstNode *node = new Yosys::AST::AstNode(Yosys::AST::AST_TERNARY, children_list.at(2)); - if (ast_node->str == "bufif0") { - node->children.push_back(Yosys::AST::AstNode::mkconst_bits(z_const, false)); - node->children.push_back(mux_input); - } else { - node->children.push_back(mux_input); - node->children.push_back(Yosys::AST::AstNode::mkconst_bits(z_const, false)); - } - - ast_node->str.clear(); - ast_node->type = Yosys::AST::AST_ASSIGN; - ast_node->children.push_back(children_list.at(0)); - ast_node->children.back()->was_checked = true; - ast_node->children.push_back(node); - did_something = true; - } - else if (ast_node->str == "buf" || ast_node->str == "not") - { - Yosys::AST::AstNode *input = children_list.back(); - if (ast_node->str == "not") - input = new Yosys::AST::AstNode(Yosys::AST::AST_BIT_NOT, input); - - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_GENBLOCK); - for (auto it = children_list.begin(); it != std::prev(children_list.end()); it++) { - newNode->children.push_back(new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN, *it, input->clone())); - newNode->children.back()->was_checked = true; - } - delete input; - - did_something = true; - } - else - { - Yosys::AST::AstNodeType op_type = Yosys::AST::AST_NONE; - bool invert_results = false; - - if (ast_node->str == "and") - op_type = Yosys::AST::AST_BIT_AND; - if (ast_node->str == "nand") - op_type = Yosys::AST::AST_BIT_AND, invert_results = true; - if (ast_node->str == "or") - op_type = Yosys::AST::AST_BIT_OR; - if (ast_node->str == "nor") - op_type = Yosys::AST::AST_BIT_OR, invert_results = true; - if (ast_node->str == "xor") - op_type = Yosys::AST::AST_BIT_XOR; - if (ast_node->str == "xnor") - op_type = Yosys::AST::AST_BIT_XOR, invert_results = true; - log_assert(op_type != Yosys::AST::AST_NONE); - - Yosys::AST::AstNode *node = children_list[1]; - if (op_type != Yosys::AST::AST_POS) - for (size_t i = 2; i < children_list.size(); i++) { - node = new Yosys::AST::AstNode(op_type, node, children_list[i]); - node->location = ast_node->location; - } - if (invert_results) - node = new Yosys::AST::AstNode(Yosys::AST::AST_BIT_NOT, node); - - ast_node->str.clear(); - ast_node->type = Yosys::AST::AST_ASSIGN; - ast_node->children.push_back(children_list[0]); - ast_node->children.back()->was_checked = true; - ast_node->children.push_back(node); - did_something = true; - } - } - - // replace dynamic ranges in left-hand side expressions (e.g. "foo[bar] <= 1'b1;") with - // either a big case block that selects the correct single-bit assignment, or mask and - // shift operations. - if (ast_node->type == Yosys::AST::AST_ASSIGN_EQ || ast_node->type == Yosys::AST::AST_ASSIGN_LE) - { - if (ast_node->children[0]->type != Yosys::AST::AST_IDENTIFIER || ast_node->children[0]->children.size() == 0) - goto skip_dynamic_range_lvalue_expansion; - if (ast_node->children[0]->children[0]->range_valid || did_something) - goto skip_dynamic_range_lvalue_expansion; - if (ast_node->children[0]->id2ast == NULL || ast_node->children[0]->id2ast->type != Yosys::AST::AST_WIRE) - goto skip_dynamic_range_lvalue_expansion; - if (!ast_node->children[0]->id2ast->range_valid) - goto skip_dynamic_range_lvalue_expansion; - - int source_width = ast_node->children[0]->id2ast->range_left - ast_node->children[0]->id2ast->range_right + 1; - int source_offset = ast_node->children[0]->id2ast->range_right; - int result_width = 1; - Yosys::AST::AstNode *member_node = systemverilog_plugin::get_struct_member(ast_node->children[0]); - if (member_node) { - // Clamp chunk to range of member within struct/union. - log_assert(!source_offset && !ast_node->children[0]->id2ast->range_swapped); - source_width = member_node->range_left - member_node->range_right + 1; - } - - Yosys::AST::AstNode *shift_expr = NULL; - Yosys::AST::AstNode *range = ast_node->children[0]->children[0]; - - if (range->children.size() == 1) { - shift_expr = range->children[0]->clone(); - } else { - shift_expr = range->children[1]->clone(); - Yosys::AST::AstNode *left_at_zero_ast = range->children[0]->clone(); - Yosys::AST::AstNode *right_at_zero_ast = range->children[1]->clone(); - while (simplify(left_at_zero_ast, true, true, false, stage, -1, false, false)) { } - while (simplify(right_at_zero_ast, true, true, false, stage, -1, false, false)) { } - if (left_at_zero_ast->type != Yosys::AST::AST_CONSTANT || right_at_zero_ast->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", ast_node->str.c_str()); - result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; - delete left_at_zero_ast; - delete right_at_zero_ast; - } - - bool use_case_method = false; - - if (ast_node->children[0]->id2ast->attributes.count(ID::nowrshmsk)) { - Yosys::AST::AstNode *node = ast_node->children[0]->id2ast->attributes.at(ID::nowrshmsk); - while (simplify(node, true, false, false, stage, -1, false, false)) { } - if (node->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Non-constant value for `nowrshmsk' attribute on `%s'!\n", ast_node->children[0]->id2ast->str.c_str()); - if (node->asAttrConst().as_bool()) - use_case_method = true; - } - - if (!use_case_method && current_always->detect_latch(ast_node->children[0]->str)) - use_case_method = true; - - if (use_case_method) - { - // big case block - - did_something = true; - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_CASE, shift_expr); - for (int i = 0; i < source_width; i++) { - int start_bit = source_offset + i; - int end_bit = std::min(start_bit+result_width,source_width) - 1; - Yosys::AST::AstNode *cond = new Yosys::AST::AstNode(Yosys::AST::AST_COND, ast_node->mkconst_int(start_bit, true)); - Yosys::AST::AstNode *lvalue = ast_node->children[0]->clone(); - lvalue->delete_children(); - if (member_node) - lvalue->attributes[ID::wiretype] = member_node->clone(); - lvalue->children.push_back(new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, - ast_node->mkconst_int(end_bit, true), ast_node->mkconst_int(start_bit, true))); - cond->children.push_back(new Yosys::AST::AstNode(Yosys::AST::AST_BLOCK, new Yosys::AST::AstNode(ast_node->type, lvalue, ast_node->children[1]->clone()))); - newNode->children.push_back(cond); - } - } - else - { - // mask and shift operations, disabled for now - - Yosys::AST::AstNode *wire_mask = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE, new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, ast_node->mkconst_int(source_width-1, true), ast_node->mkconst_int(0, true))); - wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", encode_filename(ast_node->filename).c_str(), ast_node->location.first_line, autoidx++); - wire_mask->attributes[ID::nosync] = ast_node->mkconst_int(1, false); - wire_mask->is_logic = true; - while (simplify(wire_mask, true, false, false, 1, -1, false, false)) { } - current_ast_mod->children.push_back(wire_mask); - - Yosys::AST::AstNode *wire_data = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE, new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, ast_node->mkconst_int(source_width-1, true), ast_node->mkconst_int(0, true))); - wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", encode_filename(ast_node->filename).c_str(), ast_node->location.first_line, autoidx++); - wire_data->attributes[ID::nosync] = ast_node->mkconst_int(1, false); - wire_data->is_logic = true; - while (simplify(wire_data, true, false, false, 1, -1, false, false)) { } - current_ast_mod->children.push_back(wire_data); - - int shamt_width_hint = -1; - bool shamt_sign_hint = true; - shift_expr->detectSignWidth(shamt_width_hint, shamt_sign_hint); - - Yosys::AST::AstNode *wire_sel = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE, new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, ast_node->mkconst_int(shamt_width_hint-1, true), ast_node->mkconst_int(0, true))); - wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", encode_filename(ast_node->filename).c_str(), ast_node->location.first_line, autoidx++); - wire_sel->attributes[ID::nosync] = ast_node->mkconst_int(1, false); - wire_sel->is_logic = true; - wire_sel->is_signed = shamt_sign_hint; - while (simplify(wire_sel, true, false, false, 1, -1, false, false)) { } - current_ast_mod->children.push_back(wire_sel); - - did_something = true; - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_BLOCK); - - Yosys::AST::AstNode *lvalue = ast_node->children[0]->clone(); - lvalue->delete_children(); - if (member_node) - lvalue->attributes[ID::wiretype] = member_node->clone(); - - Yosys::AST::AstNode *ref_mask = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - ref_mask->str = wire_mask->str; - ref_mask->id2ast = wire_mask; - ref_mask->was_checked = true; - - Yosys::AST::AstNode *ref_data = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - ref_data->str = wire_data->str; - ref_data->id2ast = wire_data; - ref_data->was_checked = true; - - Yosys::AST::AstNode *ref_sel = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - ref_sel->str = wire_sel->str; - ref_sel->id2ast = wire_sel; - ref_sel->was_checked = true; - - Yosys::AST::AstNode *old_data = lvalue->clone(); - if (ast_node->type == Yosys::AST::AST_ASSIGN_LE) - old_data->lookahead = true; - - Yosys::AST::AstNode *s = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, ref_sel->clone(), shift_expr); - newNode->children.push_back(s); - - Yosys::AST::AstNode *shamt = ref_sel; - - // convert to signed while preserving the sign and value - shamt = new Yosys::AST::AstNode(Yosys::AST::AST_CAST_SIZE, ast_node->mkconst_int(shamt_width_hint + 1, true), shamt); - shamt = new Yosys::AST::AstNode(Yosys::AST::AST_TO_SIGNED, shamt); - - // offset the shift amount by the lower bound of the dimension - int start_bit = source_offset; - shamt = new Yosys::AST::AstNode(Yosys::AST::AST_SUB, shamt, ast_node->mkconst_int(start_bit, true)); - - // reflect the shift amount if the dimension is swapped - if (ast_node->children[0]->id2ast->range_swapped) - shamt = new Yosys::AST::AstNode(Yosys::AST::AST_SUB, ast_node->mkconst_int(source_width - result_width, true), shamt); - - // AST_SHIFT uses negative amounts for shifting left - shamt = new Yosys::AST::AstNode(Yosys::AST::AST_NEG, shamt); - - Yosys::AST::AstNode *t; - - t = Yosys::AST::AstNode::mkconst_bits(std::vector(result_width, State::S1), false); - t = new Yosys::AST::AstNode(Yosys::AST::AST_SHIFT, t, shamt->clone()); - t = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, ref_mask->clone(), t); - newNode->children.push_back(t); - - t = new Yosys::AST::AstNode(Yosys::AST::AST_BIT_AND, Yosys::AST::AstNode::mkconst_bits(std::vector(result_width, State::S1), false), ast_node->children[1]->clone()); - t = new Yosys::AST::AstNode(Yosys::AST::AST_SHIFT, t, shamt); - t = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, ref_data->clone(), t); - newNode->children.push_back(t); - - t = new Yosys::AST::AstNode(Yosys::AST::AST_BIT_AND, old_data, new Yosys::AST::AstNode(Yosys::AST::AST_BIT_NOT, ref_mask)); - t = new Yosys::AST::AstNode(Yosys::AST::AST_BIT_OR, t, ref_data); - t = new Yosys::AST::AstNode(ast_node->type, lvalue, t); - newNode->children.push_back(t); - } - - goto apply_newNode; - } -skip_dynamic_range_lvalue_expansion:; - - if (stage > 1 && (ast_node->type == Yosys::AST::AST_ASSERT || ast_node->type == Yosys::AST::AST_ASSUME || ast_node->type == Yosys::AST::AST_LIVE || ast_node->type == Yosys::AST::AST_FAIR || ast_node->type == Yosys::AST::AST_COVER) && current_block != NULL) - { - std::stringstream sstr; - sstr << "$formal$" << encode_filename(ast_node->filename) << ":" << ast_node->location.first_line << "$" << (autoidx++); - std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN"; - - Yosys::AST::AstNode *wire_check = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE); - wire_check->str = id_check; - wire_check->was_checked = true; - current_ast_mod->children.push_back(wire_check); - current_scope[wire_check->str] = wire_check; - while (simplify(wire_check, true, false, false, 1, -1, false, false)) { } - - Yosys::AST::AstNode *wire_en = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE); - wire_en->str = id_en; - wire_en->was_checked = true; - current_ast_mod->children.push_back(wire_en); - if (current_always_clocked) { - current_ast_mod->children.push_back(new Yosys::AST::AstNode(Yosys::AST::AST_INITIAL, new Yosys::AST::AstNode(Yosys::AST::AST_BLOCK, new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_LE, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), ast_node->mkconst_int(0, false, 1))))); - current_ast_mod->children.back()->children[0]->children[0]->children[0]->str = id_en; - current_ast_mod->children.back()->children[0]->children[0]->children[0]->was_checked = true; - } - current_scope[wire_en->str] = wire_en; - while (simplify(wire_en, true, false, false, 1, -1, false, false)) { } - - Yosys::AST::AstNode *check_defval; - if (ast_node->type == Yosys::AST::AST_LIVE || ast_node->type == Yosys::AST::AST_FAIR) { - check_defval = new Yosys::AST::AstNode(Yosys::AST::AST_REDUCE_BOOL, ast_node->children[0]->clone()); - } else { - std::vector x_bit; - x_bit.push_back(RTLIL::State::Sx); - check_defval = Yosys::AST::AstNode::mkconst_bits(x_bit, false); - } - - Yosys::AST::AstNode *assign_check = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_LE, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), check_defval); - assign_check->children[0]->str = id_check; - assign_check->children[0]->was_checked = true; - - Yosys::AST::AstNode *assign_en = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_LE, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), ast_node->mkconst_int(0, false, 1)); - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; - - Yosys::AST::AstNode *default_signals = new Yosys::AST::AstNode(Yosys::AST::AST_BLOCK); - default_signals->children.push_back(assign_check); - default_signals->children.push_back(assign_en); - current_top_block->children.insert(current_top_block->children.begin(), default_signals); - - if (ast_node->type == Yosys::AST::AST_LIVE || ast_node->type == Yosys::AST::AST_FAIR) { - assign_check = nullptr; - } else { - assign_check = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_LE, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), new Yosys::AST::AstNode(Yosys::AST::AST_REDUCE_BOOL, ast_node->children[0]->clone())); - assign_check->children[0]->str = id_check; - assign_check->children[0]->was_checked = true; - } - - if (current_always == nullptr || current_always->type != Yosys::AST::AST_INITIAL) { - assign_en = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_LE, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), ast_node->mkconst_int(1, false, 1)); - } else { - assign_en = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_LE, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), new Yosys::AST::AstNode(Yosys::AST::AST_FCALL)); - assign_en->children[1]->str = "\\$initstate"; - } - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; - - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_BLOCK); - if (assign_check != nullptr) - newNode->children.push_back(assign_check); - newNode->children.push_back(assign_en); - - Yosys::AST::AstNode *assertnode = new Yosys::AST::AstNode(ast_node->type); - assertnode->location = ast_node->location; - assertnode->str = ast_node->str; - assertnode->children.push_back(new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER)); - assertnode->children.push_back(new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER)); - assertnode->children[0]->str = id_check; - assertnode->children[1]->str = id_en; - assertnode->attributes.swap(ast_node->attributes); - current_ast_mod->children.push_back(assertnode); - - goto apply_newNode; - } - - if (stage > 1 && (ast_node->type == Yosys::AST::AST_ASSERT || ast_node->type == Yosys::AST::AST_ASSUME || ast_node->type == Yosys::AST::AST_LIVE || ast_node->type == Yosys::AST::AST_FAIR || ast_node->type == Yosys::AST::AST_COVER) && ast_node->children.size() == 1) - { - ast_node->children.push_back(ast_node->mkconst_int(1, false, 1)); - did_something = true; - } - - // found right-hand side identifier for memory -> replace with memory read port - if (stage > 1 && ast_node->type == Yosys::AST::AST_IDENTIFIER && ast_node->id2ast != NULL && ast_node->id2ast->type == Yosys::AST::AST_MEMORY && !in_lvalue && - ast_node->children.size() == 1 && ast_node->children[0]->type == Yosys::AST::AST_RANGE && ast_node->children[0]->children.size() == 1) { - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_MEMRD, ast_node->children[0]->children[0]->clone()); - newNode->str = ast_node->str; - newNode->id2ast = ast_node->id2ast; - goto apply_newNode; - } - - // assignment with nontrivial member in left-hand concat expression -> split assignment - if ((ast_node->type == Yosys::AST::AST_ASSIGN_EQ || ast_node->type == Yosys::AST::AST_ASSIGN_LE) && ast_node->children[0]->type == Yosys::AST::AST_CONCAT && width_hint > 0) - { - bool found_nontrivial_member = false; - - for (auto child : ast_node->children[0]->children) { - if (child->type == Yosys::AST::AST_IDENTIFIER && child->id2ast != NULL && child->id2ast->type == Yosys::AST::AST_MEMORY) - found_nontrivial_member = true; - } - - if (found_nontrivial_member) - { - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_BLOCK); - - Yosys::AST::AstNode *wire_tmp = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE, new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, ast_node->mkconst_int(width_hint-1, true), ast_node->mkconst_int(0, true))); - wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", encode_filename(ast_node->filename).c_str(), ast_node->location.first_line, autoidx++); - current_ast_mod->children.push_back(wire_tmp); - current_scope[wire_tmp->str] = wire_tmp; - wire_tmp->attributes[ID::nosync] = ast_node->mkconst_int(1, false); - while (simplify(wire_tmp, true, false, false, 1, -1, false, false)) { } - wire_tmp->is_logic = true; - - Yosys::AST::AstNode *wire_tmp_id = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - wire_tmp_id->str = wire_tmp->str; - - newNode->children.push_back(new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, wire_tmp_id, ast_node->children[1]->clone())); - newNode->children.back()->was_checked = true; - - int cursor = 0; - for (auto child : ast_node->children[0]->children) - { - int child_width_hint = -1; - bool child_sign_hint = true; - child->detectSignWidth(child_width_hint, child_sign_hint); - - Yosys::AST::AstNode *rhs = wire_tmp_id->clone(); - rhs->children.push_back(new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, ast_node->mkconst_int(cursor+child_width_hint-1, true), ast_node->mkconst_int(cursor, true))); - newNode->children.push_back(new Yosys::AST::AstNode(ast_node->type, child->clone(), rhs)); - - cursor += child_width_hint; - } - - goto apply_newNode; - } - } - - // assignment with memory in left-hand side expression -> replace with memory write port - if (stage > 1 && (ast_node->type == Yosys::AST::AST_ASSIGN_EQ || ast_node->type == Yosys::AST::AST_ASSIGN_LE) && ast_node->children[0]->type == Yosys::AST::AST_IDENTIFIER && - ast_node->children[0]->id2ast && ast_node->children[0]->id2ast->type == Yosys::AST::AST_MEMORY && ast_node->children[0]->id2ast->children.size() >= 2 && - ast_node->children[0]->id2ast->children[0]->range_valid && ast_node->children[0]->id2ast->children[1]->range_valid && - (ast_node->children[0]->children.size() == 1 || ast_node->children[0]->children.size() == 2) && ast_node->children[0]->children[0]->type == Yosys::AST::AST_RANGE) - { - std::stringstream sstr; - sstr << "$memwr$" << ast_node->children[0]->str << "$" << encode_filename(ast_node->filename) << ":" << ast_node->location.first_line << "$" << (autoidx++); - std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN"; - - int mem_width, mem_size, addr_bits; - bool mem_signed = ast_node->children[0]->id2ast->is_signed; - ast_node->children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); - - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_BLOCK); - Yosys::AST::AstNode *defNode = new Yosys::AST::AstNode(Yosys::AST::AST_BLOCK); - - int data_range_left = ast_node->children[0]->id2ast->children[0]->range_left; - int data_range_right = ast_node->children[0]->id2ast->children[0]->range_right; - int mem_data_range_offset = std::min(data_range_left, data_range_right); - - int addr_width_hint = -1; - bool addr_sign_hint = true; - ast_node->children[0]->children[0]->children[0]->detectSignWidthWorker(addr_width_hint, addr_sign_hint); - addr_bits = std::max(addr_bits, addr_width_hint); - - std::vector x_bits_addr, x_bits_data, set_bits_en; - for (int i = 0; i < addr_bits; i++) - x_bits_addr.push_back(RTLIL::State::Sx); - for (int i = 0; i < mem_width; i++) - x_bits_data.push_back(RTLIL::State::Sx); - for (int i = 0; i < mem_width; i++) - set_bits_en.push_back(RTLIL::State::S1); - - Yosys::AST::AstNode *node_addr = nullptr; - if (ast_node->children[0]->children[0]->children[0]->isConst()) { - node_addr = ast_node->children[0]->children[0]->children[0]->clone(); - } else { - Yosys::AST::AstNode *wire_addr = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE, new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, ast_node->mkconst_int(addr_bits-1, true), ast_node->mkconst_int(0, true))); - wire_addr->str = id_addr; - wire_addr->was_checked = true; - current_ast_mod->children.push_back(wire_addr); - current_scope[wire_addr->str] = wire_addr; - while (simplify(wire_addr, true, false, false, 1, -1, false, false)) { } - - Yosys::AST::AstNode *assign_addr = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), Yosys::AST::AstNode::mkconst_bits(x_bits_addr, false)); - assign_addr->children[0]->str = id_addr; - assign_addr->children[0]->was_checked = true; - defNode->children.push_back(assign_addr); - - assign_addr = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), ast_node->children[0]->children[0]->children[0]->clone()); - assign_addr->children[0]->str = id_addr; - assign_addr->children[0]->was_checked = true; - newNode->children.push_back(assign_addr); - - node_addr = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - node_addr->str = id_addr; - } - - Yosys::AST::AstNode *node_data = nullptr; - if (ast_node->children[0]->children.size() == 1 && ast_node->children[1]->isConst()) { - node_data = ast_node->children[1]->clone(); - } else { - Yosys::AST::AstNode *wire_data = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE, new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, ast_node->mkconst_int(mem_width-1, true), ast_node->mkconst_int(0, true))); - wire_data->str = id_data; - wire_data->was_checked = true; - wire_data->is_signed = mem_signed; - current_ast_mod->children.push_back(wire_data); - current_scope[wire_data->str] = wire_data; - while (simplify(wire_data, true, false, false, 1, -1, false, false)) { } - - Yosys::AST::AstNode *assign_data = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), Yosys::AST::AstNode::mkconst_bits(x_bits_data, false)); - assign_data->children[0]->str = id_data; - assign_data->children[0]->was_checked = true; - defNode->children.push_back(assign_data); - - node_data = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - node_data->str = id_data; - } - - Yosys::AST::AstNode *wire_en = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE, new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, ast_node->mkconst_int(mem_width-1, true), ast_node->mkconst_int(0, true))); - wire_en->str = id_en; - wire_en->was_checked = true; - current_ast_mod->children.push_back(wire_en); - current_scope[wire_en->str] = wire_en; - while (simplify(wire_en, true, false, false, 1, -1, false, false)) { } - - Yosys::AST::AstNode *assign_en_first = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), ast_node->mkconst_int(0, false, mem_width)); - assign_en_first->children[0]->str = id_en; - assign_en_first->children[0]->was_checked = true; - defNode->children.push_back(assign_en_first); - - Yosys::AST::AstNode *node_en = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - node_en->str = id_en; - - if (!defNode->children.empty()) - current_top_block->children.insert(current_top_block->children.begin(), defNode); - else - delete defNode; - - Yosys::AST::AstNode *assign_data = nullptr; - Yosys::AST::AstNode *assign_en = nullptr; - if (ast_node->children[0]->children.size() == 2) - { - if (ast_node->children[0]->children[1]->range_valid) - { - int offset = ast_node->children[0]->children[1]->range_right; - int width = ast_node->children[0]->children[1]->range_left - offset + 1; - offset -= mem_data_range_offset; - - std::vector padding_x(offset, RTLIL::State::Sx); - - assign_data = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), - new Yosys::AST::AstNode(Yosys::AST::AST_CONCAT, Yosys::AST::AstNode::mkconst_bits(padding_x, false), ast_node->children[1]->clone())); - assign_data->children[0]->str = id_data; - assign_data->children[0]->was_checked = true; - - for (int i = 0; i < mem_width; i++) - set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0; - assign_en = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), Yosys::AST::AstNode::mkconst_bits(set_bits_en, false)); - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; - } - else - { - Yosys::AST::AstNode *the_range = ast_node->children[0]->children[1]; - Yosys::AST::AstNode *left_at_zero_ast = the_range->children[0]->clone(); - Yosys::AST::AstNode *right_at_zero_ast = the_range->children.size() >= 2 ? the_range->children[1]->clone() : left_at_zero_ast->clone(); - Yosys::AST::AstNode *offset_ast = right_at_zero_ast->clone(); - - if (mem_data_range_offset) - offset_ast = new Yosys::AST::AstNode(Yosys::AST::AST_SUB, offset_ast, ast_node->mkconst_int(mem_data_range_offset, true)); - - while (simplify(left_at_zero_ast, true, true, false, 1, -1, false, false)) { } - while (simplify(right_at_zero_ast, true, true, false, 1, -1, false, false)) { } - if (left_at_zero_ast->type != Yosys::AST::AST_CONSTANT || right_at_zero_ast->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", ast_node->str.c_str()); - int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; - - assign_data = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), - new Yosys::AST::AstNode(Yosys::AST::AST_SHIFT_LEFT, ast_node->children[1]->clone(), offset_ast->clone())); - assign_data->children[0]->str = id_data; - assign_data->children[0]->was_checked = true; - - for (int i = 0; i < mem_width; i++) - set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0; - assign_en = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), - new Yosys::AST::AstNode(Yosys::AST::AST_SHIFT_LEFT, Yosys::AST::AstNode::mkconst_bits(set_bits_en, false), offset_ast->clone())); - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; - - delete left_at_zero_ast; - delete right_at_zero_ast; - delete offset_ast; - } - } - else - { - if (!(ast_node->children[0]->children.size() == 1 && ast_node->children[1]->isConst())) { - assign_data = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), ast_node->children[1]->clone()); - assign_data->children[0]->str = id_data; - assign_data->children[0]->was_checked = true; - } - - assign_en = new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER), Yosys::AST::AstNode::mkconst_bits(set_bits_en, false)); - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; - } - if (assign_data) - newNode->children.push_back(assign_data); - if (assign_en) - newNode->children.push_back(assign_en); - - Yosys::AST::AstNode *wrnode; - if (current_always->type == Yosys::AST::AST_INITIAL) - wrnode = new Yosys::AST::AstNode(Yosys::AST::AST_MEMINIT, node_addr, node_data, node_en, ast_node->mkconst_int(1, false)); - else - wrnode = new Yosys::AST::AstNode(Yosys::AST::AST_MEMWR, node_addr, node_data, node_en); - wrnode->str = ast_node->children[0]->str; - wrnode->id2ast = ast_node->children[0]->id2ast; - wrnode->location = ast_node->location; - if (wrnode->type == Yosys::AST::AST_MEMWR) { - int portid = current_memwr_count[wrnode->str]++; - wrnode->children.push_back(ast_node->mkconst_int(portid, false)); - std::vector priority_mask; - for (int i = 0; i < portid; i++) { - bool has_prio = current_memwr_visible[wrnode->str].count(i); - priority_mask.push_back(State(has_prio)); - } - wrnode->children.push_back(Yosys::AST::AstNode::mkconst_bits(priority_mask, false)); - current_memwr_visible[wrnode->str].insert(portid); - current_always->children.push_back(wrnode); - } else { - current_ast_mod->children.push_back(wrnode); - } - - if (newNode->children.empty()) { - delete newNode; - newNode = new Yosys::AST::AstNode(); - } - goto apply_newNode; - } - - // replace function and task calls with the code from the function or task - if ((ast_node->type == Yosys::AST::AST_FCALL || ast_node->type == Yosys::AST::AST_TCALL) && !ast_node->str.empty()) - { - if (ast_node->type == Yosys::AST::AST_FCALL) - { - if (ast_node->str == "\\$initstate") - { - int myidx = autoidx++; - - Yosys::AST::AstNode *wire = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE); - wire->str = stringf("$initstate$%d_wire", myidx); - current_ast_mod->children.push_back(wire); - while (simplify(wire, true, false, false, 1, -1, false, false)) { } - - Yosys::AST::AstNode *cell = new Yosys::AST::AstNode(Yosys::AST::AST_CELL, new Yosys::AST::AstNode(Yosys::AST::AST_CELLTYPE), new Yosys::AST::AstNode(Yosys::AST::AST_ARGUMENT, new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER))); - cell->str = stringf("$initstate$%d", myidx); - cell->children[0]->str = "$initstate"; - cell->children[1]->str = "\\Y"; - cell->children[1]->children[0]->str = wire->str; - cell->children[1]->children[0]->id2ast = wire; - current_ast_mod->children.push_back(cell); - while (simplify(cell, true, false, false, 1, -1, false, false)) { } - - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - newNode->str = wire->str; - newNode->id2ast = wire; - goto apply_newNode; - } - - if (ast_node->str == "\\$past") - { - if (width_hint < 0) - goto replace_fcall_later; - - int num_steps = 1; - - if (GetSize(ast_node->children) != 1 && GetSize(ast_node->children) != 2) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s got %d arguments, expected 1 or 2.\n", - RTLIL::unescape_id(ast_node->str).c_str(), int(ast_node->children.size())); - - if (!current_always_clocked) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s is only allowed in clocked blocks.\n", - RTLIL::unescape_id(ast_node->str).c_str()); - - if (GetSize(ast_node->children) == 2) - { - Yosys::AST::AstNode *buf = ast_node->children[1]->clone(); - while (simplify(buf, true, false, false, stage, -1, false, false)) { } - if (buf->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system function `%s' with non-constant value.\n", ast_node->str.c_str()); - - num_steps = buf->asInt(true); - delete buf; - } - - Yosys::AST::AstNode *block = nullptr; - - for (auto child : current_always->children) - if (child->type == Yosys::AST::AST_BLOCK) - block = child; - - log_assert(block != nullptr); - - if (num_steps == 0) { - newNode = ast_node->children[0]->clone(); - goto apply_newNode; - } - - int myidx = autoidx++; - Yosys::AST::AstNode *outreg = nullptr; - - for (int i = 0; i < num_steps; i++) - { - Yosys::AST::AstNode *reg = new Yosys::AST::AstNode(Yosys::AST::AST_WIRE, new Yosys::AST::AstNode(Yosys::AST::AST_RANGE, - ast_node->mkconst_int(width_hint-1, true), ast_node->mkconst_int(0, true))); - - reg->str = stringf("$past$%s:%d$%d$%d", encode_filename(ast_node->filename).c_str(), ast_node->location.first_line, myidx, i); - reg->is_reg = true; - reg->is_signed = sign_hint; - - current_ast_mod->children.push_back(reg); - - while (simplify(reg, true, false, false, 1, -1, false, false)) { } - - Yosys::AST::AstNode *regid = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - regid->str = reg->str; - regid->id2ast = reg; - regid->was_checked = true; - - Yosys::AST::AstNode *rhs = nullptr; - - if (outreg == nullptr) { - rhs = ast_node->children.at(0)->clone(); - } else { - rhs = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - rhs->str = outreg->str; - rhs->id2ast = outreg; - } - - block->children.push_back(new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_LE, regid, rhs)); - outreg = reg; - } - - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - newNode->str = outreg->str; - newNode->id2ast = outreg; - goto apply_newNode; - } - - if (ast_node->str == "\\$stable" || ast_node->str == "\\$rose" || ast_node->str == "\\$fell" || ast_node->str == "\\$changed") - { - if (GetSize(ast_node->children) != 1) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(ast_node->str).c_str(), int(ast_node->children.size())); - - if (!current_always_clocked) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s is only allowed in clocked blocks.\n", - RTLIL::unescape_id(ast_node->str).c_str()); - - Yosys::AST::AstNode *present = ast_node->children.at(0)->clone(); - Yosys::AST::AstNode *past = ast_node->clone(); - past->str = "\\$past"; - - if (ast_node->str == "\\$stable") - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_EQ, past, present); - - else if (ast_node->str == "\\$changed") - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_NE, past, present); - - else if (ast_node->str == "\\$rose") - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_LOGIC_AND, - new Yosys::AST::AstNode(Yosys::AST::AST_LOGIC_NOT, new Yosys::AST::AstNode(Yosys::AST::AST_BIT_AND, past, ast_node->mkconst_int(1,false))), - new Yosys::AST::AstNode(Yosys::AST::AST_BIT_AND, present, ast_node->mkconst_int(1,false))); - - else if (ast_node->str == "\\$fell") - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_LOGIC_AND, - new Yosys::AST::AstNode(Yosys::AST::AST_BIT_AND, past, ast_node->mkconst_int(1,false)), - new Yosys::AST::AstNode(Yosys::AST::AST_LOGIC_NOT, new Yosys::AST::AstNode(Yosys::AST::AST_BIT_AND, present, ast_node->mkconst_int(1,false)))); - - else - log_abort(); - - goto apply_newNode; - } - - // $anyconst and $anyseq are mapped in genRTLIL() - if (ast_node->str == "\\$anyconst" || ast_node->str == "\\$anyseq" || ast_node->str == "\\$allconst" || ast_node->str == "\\$allseq") { - recursion_counter--; - return false; - } - - if (ast_node->str == "\\$clog2") - { - if (ast_node->children.size() != 1) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(ast_node->str).c_str(), int(ast_node->children.size())); - - Yosys::AST::AstNode *buf = ast_node->children[0]->clone(); - while (simplify(buf, true, false, false, stage, width_hint, sign_hint, false)) { } - if (buf->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system function `%s' with non-constant value.\n", ast_node->str.c_str()); - - RTLIL::Const arg_value = buf->bitsAsConst(); - if (arg_value.as_bool()) - arg_value = const_sub(arg_value, 1, false, false, GetSize(arg_value)); - delete buf; - - uint32_t result = 0; - for (size_t i = 0; i < arg_value.bits.size(); i++) - if (arg_value.bits.at(i) == RTLIL::State::S1) - result = i + 1; - - newNode = ast_node->mkconst_int(result, true); - goto apply_newNode; - } - - if (ast_node->str == "\\$size" || ast_node->str == "\\$bits" || ast_node->str == "\\$high" || ast_node->str == "\\$low" || ast_node->str == "\\$left" || ast_node->str == "\\$right") - { - int dim = 1; - if (ast_node->str == "\\$bits") { - if (ast_node->children.size() != 1) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(ast_node->str).c_str(), int(ast_node->children.size())); - } else { - if (ast_node->children.size() != 1 && ast_node->children.size() != 2) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s got %d arguments, expected 1 or 2.\n", - RTLIL::unescape_id(ast_node->str).c_str(), int(ast_node->children.size())); - if (ast_node->children.size() == 2) { - Yosys::AST::AstNode *buf = ast_node->children[1]->clone(); - // Evaluate constant expression - while (simplify(buf, true, false, false, stage, width_hint, sign_hint, false)) { } - dim = buf->asInt(false); - delete buf; - } - } - Yosys::AST::AstNode *buf = ast_node->children[0]->clone(); - int mem_depth = 1; - int result, high = 0, low = 0, left = 0, right = 0, width = 1; // defaults for a simple wire - Yosys::AST::AstNode *id_ast = NULL; - - // Is this needed? - //while (simplify(buf, true, false, false, stage, width_hint, sign_hint, false)) { } - buf->detectSignWidth(width_hint, sign_hint); - - if (buf->type == Yosys::AST::AST_IDENTIFIER) { - id_ast = buf->id2ast; - if (id_ast == NULL && current_scope.count(buf->str)) - id_ast = current_scope.at(buf->str); - if (!id_ast) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to resolve identifier %s for width detection!\n", buf->str.c_str()); - - // Check for item in packed struct / union - Yosys::AST::AstNode *item_node = systemverilog_plugin::get_struct_member(buf); - if (id_ast->type == Yosys::AST::AST_WIRE && item_node) { - // The dimension of the original array expression is saved in the 'integer' field - dim += buf->integer; - if (item_node->multirange_dimensions.empty()) { - if (dim != 1) - log_file_error(ast_node->filename, ast_node->location.first_line, "Dimension %d out of range in `%s', as it only has one dimension!\n", dim, item_node->str.c_str()); - left = high = item_node->range_left; - right = low = item_node->range_right; - } else { - int dims = GetSize(item_node->multirange_dimensions)/2; - if (dim < 1 || dim > dims) - log_file_error(ast_node->filename, ast_node->location.first_line, "Dimension %d out of range in `%s', as it only has dimensions 1..%d!\n", dim, item_node->str.c_str(), dims); - right = low = get_struct_range_offset(item_node, dim - 1); - left = high = low + get_struct_range_width(item_node, dim - 1) - 1; - if (item_node->multirange_swapped[dim - 1]) { - std::swap(left, right); - } - for (int i = dim; i < dims; i++) { - mem_depth *= get_struct_range_width(item_node, i); - } - } - } - // Otherwise, we have 4 cases: - // wire x; ==> AST_WIRE, no AST_RANGE children - // wire [1:0]x; ==> AST_WIRE, AST_RANGE children - // wire [1:0]x[1:0]; ==> AST_MEMORY, two AST_RANGE children (1st for packed, 2nd for unpacked) - // wire [1:0]x[1:0][1:0]; ==> AST_MEMORY, one AST_RANGE child (0) for packed, then AST_MULTIRANGE child (1) for unpacked - // (updated: actually by the time we are here, AST_MULTIRANGE is converted into one big AST_RANGE) - // case 0 handled by default - else if ((id_ast->type == Yosys::AST::AST_WIRE || id_ast->type == Yosys::AST::AST_MEMORY) && id_ast->children.size() > 0) { - // handle packed array left/right for case 1, and cases 2/3 when requesting the last dimension (packed side) - Yosys::AST::AstNode *wire_range = id_ast->children[0]; - left = wire_range->children[0]->integer; - right = wire_range->children[1]->integer; - high = max(left, right); - low = min(left, right); - } - if (id_ast->type == Yosys::AST::AST_MEMORY) { - // a slice of our identifier means we advance to the next dimension, e.g. $size(a[3]) - if (buf->children.size() > 0) { - // something is hanging below this identifier - if (buf->children[0]->type == Yosys::AST::AST_RANGE && buf->integer == 0) - // if integer == 0, ast_node node was originally created as Yosys::AST::AST_RANGE so it's dimension is 1 - dim++; - // more than one range, e.g. $size(a[3][2]) - else // created an Yosys::AST::AST_MULTIRANGE, converted to Yosys::AST::AST_RANGE, but original dimension saved in 'integer' field - dim += buf->integer; // increment by multirange size - } - - // We got here only if the argument is a memory - // Otherwise $size() and $bits() return the expression width - Yosys::AST::AstNode *mem_range = id_ast->children[1]; - if (ast_node->str == "\\$bits") { - if (mem_range->type == Yosys::AST::AST_RANGE) { - if (!mem_range->range_valid) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to detect width of memory access `%s'!\n", buf->str.c_str()); - mem_depth = mem_range->range_left - mem_range->range_right + 1; - } else - log_file_error(ast_node->filename, ast_node->location.first_line, "Unknown memory depth AST type in `%s'!\n", buf->str.c_str()); - } else { - // $size(), $left(), $right(), $high(), $low() - int dims = 1; - if (mem_range->type == Yosys::AST::AST_RANGE) { - if (id_ast->multirange_dimensions.empty()) { - if (!mem_range->range_valid) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to detect width of memory access `%s'!\n", buf->str.c_str()); - if (dim == 1) { - left = mem_range->range_right; - right = mem_range->range_left; - high = max(left, right); - low = min(left, right); - } - } else { - dims = GetSize(id_ast->multirange_dimensions)/2; - if (dim <= dims) { - width_hint = id_ast->multirange_dimensions[2*dim-1]; - high = id_ast->multirange_dimensions[2*dim-2] + id_ast->multirange_dimensions[2*dim-1] - 1; - low = id_ast->multirange_dimensions[2*dim-2]; - if (id_ast->multirange_swapped[dim-1]) { - left = low; - right = high; - } else { - right = low; - left = high; - } - } else if ((dim > dims+1) || (dim < 0)) - log_file_error(ast_node->filename, ast_node->location.first_line, "Dimension %d out of range in `%s', as it only has dimensions 1..%d!\n", dim, buf->str.c_str(), dims+1); - } - } else { - log_file_error(ast_node->filename, ast_node->location.first_line, "Unknown memory depth AST type in `%s'!\n", buf->str.c_str()); - } - } - } - width = high - low + 1; - } else { - width = width_hint; - } - delete buf; - if (ast_node->str == "\\$high") - result = high; - else if (ast_node->str == "\\$low") - result = low; - else if (ast_node->str == "\\$left") - result = left; - else if (ast_node->str == "\\$right") - result = right; - else if (ast_node->str == "\\$size") - result = width; - else { // ast_node->str == "\\$bits" - result = width * mem_depth; - } - newNode = ast_node->mkconst_int(result, true); - goto apply_newNode; - } - - if (ast_node->str == "\\$ln" || ast_node->str == "\\$log10" || ast_node->str == "\\$exp" || ast_node->str == "\\$sqrt" || ast_node->str == "\\$pow" || - ast_node->str == "\\$floor" || ast_node->str == "\\$ceil" || ast_node->str == "\\$sin" || ast_node->str == "\\$cos" || ast_node->str == "\\$tan" || - ast_node->str == "\\$asin" || ast_node->str == "\\$acos" || ast_node->str == "\\$atan" || ast_node->str == "\\$atan2" || ast_node->str == "\\$hypot" || - ast_node->str == "\\$sinh" || ast_node->str == "\\$cosh" || ast_node->str == "\\$tanh" || ast_node->str == "\\$asinh" || ast_node->str == "\\$acosh" || ast_node->str == "\\$atanh" || - ast_node->str == "\\$rtoi" || ast_node->str == "\\$itor") - { - bool func_with_two_arguments = ast_node->str == "\\$pow" || ast_node->str == "\\$atan2" || ast_node->str == "\\$hypot"; - double x = 0, y = 0; - - if (func_with_two_arguments) { - if (ast_node->children.size() != 2) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s got %d arguments, expected 2.\n", - RTLIL::unescape_id(ast_node->str).c_str(), int(ast_node->children.size())); - } else { - if (ast_node->children.size() != 1) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(ast_node->str).c_str(), int(ast_node->children.size())); - } - - if (ast_node->children.size() >= 1) { - while (simplify(ast_node->children[0], true, false, false, stage, width_hint, sign_hint, false)) { } - if (!ast_node->children[0]->isConst()) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system function `%s' with non-constant argument.\n", - RTLIL::unescape_id(ast_node->str).c_str()); - int child_width_hint = width_hint; - bool child_sign_hint = sign_hint; - ast_node->children[0]->detectSignWidth(child_width_hint, child_sign_hint); - x = ast_node->children[0]->asReal(child_sign_hint); - } - - if (ast_node->children.size() >= 2) { - while (simplify(ast_node->children[1], true, false, false, stage, width_hint, sign_hint, false)) { } - if (!ast_node->children[1]->isConst()) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system function `%s' with non-constant argument.\n", - RTLIL::unescape_id(ast_node->str).c_str()); - int child_width_hint = width_hint; - bool child_sign_hint = sign_hint; - ast_node->children[1]->detectSignWidth(child_width_hint, child_sign_hint); - y = ast_node->children[1]->asReal(child_sign_hint); - } - - if (ast_node->str == "\\$rtoi") { - newNode = ast_node->mkconst_int(x, true); - } else { - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_REALVALUE); - if (ast_node->str == "\\$ln") newNode->realvalue = ::log(x); - else if (ast_node->str == "\\$log10") newNode->realvalue = ::log10(x); - else if (ast_node->str == "\\$exp") newNode->realvalue = ::exp(x); - else if (ast_node->str == "\\$sqrt") newNode->realvalue = ::sqrt(x); - else if (ast_node->str == "\\$pow") newNode->realvalue = ::pow(x, y); - else if (ast_node->str == "\\$floor") newNode->realvalue = ::floor(x); - else if (ast_node->str == "\\$ceil") newNode->realvalue = ::ceil(x); - else if (ast_node->str == "\\$sin") newNode->realvalue = ::sin(x); - else if (ast_node->str == "\\$cos") newNode->realvalue = ::cos(x); - else if (ast_node->str == "\\$tan") newNode->realvalue = ::tan(x); - else if (ast_node->str == "\\$asin") newNode->realvalue = ::asin(x); - else if (ast_node->str == "\\$acos") newNode->realvalue = ::acos(x); - else if (ast_node->str == "\\$atan") newNode->realvalue = ::atan(x); - else if (ast_node->str == "\\$atan2") newNode->realvalue = ::atan2(x, y); - else if (ast_node->str == "\\$hypot") newNode->realvalue = ::hypot(x, y); - else if (ast_node->str == "\\$sinh") newNode->realvalue = ::sinh(x); - else if (ast_node->str == "\\$cosh") newNode->realvalue = ::cosh(x); - else if (ast_node->str == "\\$tanh") newNode->realvalue = ::tanh(x); - else if (ast_node->str == "\\$asinh") newNode->realvalue = ::asinh(x); - else if (ast_node->str == "\\$acosh") newNode->realvalue = ::acosh(x); - else if (ast_node->str == "\\$atanh") newNode->realvalue = ::atanh(x); - else if (ast_node->str == "\\$itor") newNode->realvalue = x; - else log_abort(); - } - goto apply_newNode; - } - - if (ast_node->str == "\\$sformatf") { - Yosys::AST::AstNode *node_string = ast_node->children[0]; - while (simplify(node_string, true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_string->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system function `%s' with non-constant 1st argument.\n", ast_node->str.c_str()); - std::string sformat = node_string->bitsAsConst().decode_string(); - std::string sout = ast_node->process_format_str(sformat, 1, stage, width_hint, sign_hint); - newNode = ast_node->mkconst_str(sout); - goto apply_newNode; - } - - if (ast_node->str == "\\$countbits") { - if (ast_node->children.size() < 2) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s got %d arguments, expected at least 2.\n", - RTLIL::unescape_id(ast_node->str).c_str(), int(ast_node->children.size())); - - std::vector control_bits; - - // Determine which bits to count - for (size_t i = 1; i < ast_node->children.size(); i++) { - Yosys::AST::AstNode *node = ast_node->children[i]; - while (simplify(node, true, false, false, stage, -1, false, false)) { } - if (node->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system function `%s' with non-constant control bit argument.\n", ast_node->str.c_str()); - if (node->bits.size() != 1) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system function `%s' with control bit width != 1.\n", ast_node->str.c_str()); - control_bits.push_back(node->bits[0]); - } - - // Detect width of exp (first argument of $countbits) - int exp_width = -1; - bool exp_sign = false; - Yosys::AST::AstNode *exp = ast_node->children[0]; - exp->detectSignWidth(exp_width, exp_sign, NULL); - - newNode = ast_node->mkconst_int(0, false); - - for (int i = 0; i < exp_width; i++) { - // Generate nodes for: exp << i >> ($size(exp) - 1) - // ^^ ^^ - Yosys::AST::AstNode *lsh_node = new Yosys::AST::AstNode(Yosys::AST::AST_SHIFT_LEFT, exp->clone(), Yosys::AST::AstNode::mkconst_int(i, false)); - Yosys::AST::AstNode *rsh_node = new Yosys::AST::AstNode(Yosys::AST::AST_SHIFT_RIGHT, lsh_node, Yosys::AST::AstNode::mkconst_int(exp_width - 1, false)); - - Yosys::AST::AstNode *or_node = nullptr; - - for (RTLIL::State control_bit : control_bits) { - // Generate node for: (exp << i >> ($size(exp) - 1)) === control_bit - // ^^^ - Yosys::AST::AstNode *eq_node = new Yosys::AST::AstNode(Yosys::AST::AST_EQX, rsh_node->clone(), Yosys::AST::AstNode::mkconst_bits({control_bit}, false)); - - // Or the result for each checked bit value - if (or_node) - or_node = new Yosys::AST::AstNode(Yosys::AST::AST_LOGIC_OR, or_node, eq_node); - else - or_node = eq_node; - } - - // We should have at least one element in control_bits, - // because we checked for the number of arguments above - log_assert(or_node != nullptr); - - delete rsh_node; - - // Generate node for adding with result of previous bit - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_ADD, newNode, or_node); - } - - goto apply_newNode; - } - - if (ast_node->str == "\\$countones" || ast_node->str == "\\$isunknown" || ast_node->str == "\\$onehot" || ast_node->str == "\\$onehot0") { - if (ast_node->children.size() != 1) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s got %d arguments, expected 1.\n", - RTLIL::unescape_id(ast_node->str).c_str(), int(ast_node->children.size())); - - Yosys::AST::AstNode *countbits = ast_node->clone(); - countbits->str = "\\$countbits"; - - if (ast_node->str == "\\$countones") { - countbits->children.push_back(Yosys::AST::AstNode::mkconst_bits({RTLIL::State::S1}, false)); - newNode = countbits; - } else if (ast_node->str == "\\$isunknown") { - countbits->children.push_back(Yosys::AST::AstNode::mkconst_bits({RTLIL::Sx}, false)); - countbits->children.push_back(Yosys::AST::AstNode::mkconst_bits({RTLIL::Sz}, false)); - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_GT, countbits, Yosys::AST::AstNode::mkconst_int(0, false)); - } else if (ast_node->str == "\\$onehot") { - countbits->children.push_back(Yosys::AST::AstNode::mkconst_bits({RTLIL::State::S1}, false)); - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_EQ, countbits, Yosys::AST::AstNode::mkconst_int(1, false)); - } else if (ast_node->str == "\\$onehot0") { - countbits->children.push_back(Yosys::AST::AstNode::mkconst_bits({RTLIL::State::S1}, false)); - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_LE, countbits, Yosys::AST::AstNode::mkconst_int(1, false)); - } else { - log_abort(); - } - - goto apply_newNode; - } - - if (current_scope.count(ast_node->str) != 0 && current_scope[ast_node->str]->type == Yosys::AST::AST_DPI_FUNCTION) - { - Yosys::AST::AstNode *dpi_decl = current_scope[ast_node->str]; - - std::string rtype, fname; - std::vector argtypes; - std::vector args; - - rtype = RTLIL::unescape_id(dpi_decl->children.at(0)->str); - fname = RTLIL::unescape_id(dpi_decl->children.at(1)->str); - - for (int i = 2; i < GetSize(dpi_decl->children); i++) - { - if (i-2 >= GetSize(ast_node->children)) - log_file_error(ast_node->filename, ast_node->location.first_line, "Insufficient number of arguments in DPI function call.\n"); - - argtypes.push_back(RTLIL::unescape_id(dpi_decl->children.at(i)->str)); - args.push_back(ast_node->children.at(i-2)->clone()); - while (simplify(args.back(), true, false, false, stage, -1, false, true)) { } - - if (args.back()->type != Yosys::AST::AST_CONSTANT && args.back()->type != Yosys::AST::AST_REALVALUE) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate DPI function with non-constant argument.\n"); - } - - newNode = dpi_call(rtype, fname, argtypes, args); - - for (auto arg : args) - delete arg; - - goto apply_newNode; - } - - if (current_scope.count(ast_node->str) == 0) - ast_node->str = ast_node->try_pop_module_prefix(); - if (current_scope.count(ast_node->str) == 0 || current_scope[ast_node->str]->type != Yosys::AST::AST_FUNCTION) - log_file_error(ast_node->filename, ast_node->location.first_line, "Can't resolve function name `%s'.\n", ast_node->str.c_str()); - } - - if (ast_node->type == Yosys::AST::AST_TCALL) - { - if (ast_node->str == "$finish" || ast_node->str == "$stop") - { - if (!current_always || current_always->type != Yosys::AST::AST_INITIAL) - log_file_error(ast_node->filename, ast_node->location.first_line, "System task `%s' outside initial block is unsupported.\n", ast_node->str.c_str()); - - log_file_error(ast_node->filename, ast_node->location.first_line, "System task `%s' executed.\n", ast_node->str.c_str()); - } - - if (ast_node->str == "\\$readmemh" || ast_node->str == "\\$readmemb") - { - if (GetSize(ast_node->children) < 2 || GetSize(ast_node->children) > 4) - log_file_error(ast_node->filename, ast_node->location.first_line, "System function %s got %d arguments, expected 2-4.\n", - RTLIL::unescape_id(ast_node->str).c_str(), int(ast_node->children.size())); - - Yosys::AST::AstNode *node_filename = ast_node->children[0]->clone(); - while (simplify(node_filename, true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_filename->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system function `%s' with non-constant 1st argument.\n", ast_node->str.c_str()); - - Yosys::AST::AstNode *node_memory = ast_node->children[1]->clone(); - while (simplify(node_memory, true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_memory->type != Yosys::AST::AST_IDENTIFIER || node_memory->id2ast == nullptr || node_memory->id2ast->type != Yosys::AST::AST_MEMORY) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system function `%s' with non-memory 2nd argument.\n", ast_node->str.c_str()); - - int start_addr = -1, finish_addr = -1; - - if (GetSize(ast_node->children) > 2) { - Yosys::AST::AstNode *node_addr = ast_node->children[2]->clone(); - while (simplify(node_addr, true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_addr->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system function `%s' with non-constant 3rd argument.\n", ast_node->str.c_str()); - start_addr = int(node_addr->asInt(false)); - } - - if (GetSize(ast_node->children) > 3) { - Yosys::AST::AstNode *node_addr = ast_node->children[3]->clone(); - while (simplify(node_addr, true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_addr->type != Yosys::AST::AST_CONSTANT) - log_file_error(ast_node->filename, ast_node->location.first_line, "Failed to evaluate system function `%s' with non-constant 4th argument.\n", ast_node->str.c_str()); - finish_addr = int(node_addr->asInt(false)); - } - - bool unconditional_init = false; - if (current_always->type == Yosys::AST::AST_INITIAL) { - pool queue; - log_assert(current_always->children[0]->type == Yosys::AST::AST_BLOCK); - queue.insert(current_always->children[0]); - while (!unconditional_init && !queue.empty()) { - pool next_queue; - for (auto n : queue) - for (auto c : n->children) { - if (c == ast_node) - unconditional_init = true; - next_queue.insert(c); - } - next_queue.swap(queue); - } - } - - newNode = ast_node->readmem(ast_node->str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr, unconditional_init); - delete node_filename; - delete node_memory; - goto apply_newNode; - } - - if (current_scope.count(ast_node->str) == 0) - ast_node->str = ast_node->try_pop_module_prefix(); - if (current_scope.count(ast_node->str) == 0 || current_scope[ast_node->str]->type != Yosys::AST::AST_TASK) - log_file_error(ast_node->filename, ast_node->location.first_line, "Can't resolve task name `%s'.\n", ast_node->str.c_str()); - } - - - std::stringstream sstr; - sstr << ast_node->str << "$func$" << encode_filename(ast_node->filename) << ":" << ast_node->location.first_line << "$" << (autoidx++) << '.'; - std::string prefix = sstr.str(); - - Yosys::AST::AstNode *decl = current_scope[ast_node->str]; - if (unevaluated_tern_branch && decl->is_recursive_function()) - goto replace_fcall_later; - decl = decl->clone(); - decl->replace_result_wire_name_in_function(ast_node->str, "$result"); // enables recursion - decl->expand_genblock(prefix); - - if (decl->type == Yosys::AST::AST_FUNCTION && !decl->attributes.count(ID::via_celltype)) - { - bool require_const_eval = decl->has_const_only_constructs(); - bool all_args_const = true; - for (auto child : ast_node->children) { - while (simplify(child, true, false, false, 1, -1, false, true)) { } - if (child->type != Yosys::AST::AST_CONSTANT && child->type != Yosys::AST::AST_REALVALUE) - all_args_const = false; - } - - if (all_args_const) { - Yosys::AST::AstNode *func_workspace = decl->clone(); - func_workspace->str = prefix_id(prefix, "$result"); - newNode = func_workspace->eval_const_function(ast_node, in_param || require_const_eval); - delete func_workspace; - if (newNode) { - delete decl; - goto apply_newNode; - } - } - - if (in_param) - log_file_error(ast_node->filename, ast_node->location.first_line, "Non-constant function call in constant expression.\n"); - if (require_const_eval) - log_file_error(ast_node->filename, ast_node->location.first_line, "Function %s can only be called with constant arguments.\n", ast_node->str.c_str()); - } - - size_t arg_count = 0; - dict wire_cache; - vector new_stmts; - vector output_assignments; - - if (current_block == NULL) - { - log_assert(ast_node->type == Yosys::AST::AST_FCALL); - - Yosys::AST::AstNode *wire = NULL; - std::string res_name = prefix_id(prefix, "$result"); - for (auto child : decl->children) - if (child->type == Yosys::AST::AST_WIRE && child->str == res_name) - wire = child->clone(); - log_assert(wire != NULL); - - wire->port_id = 0; - wire->is_input = false; - wire->is_output = false; - - current_scope[wire->str] = wire; - current_ast_mod->children.push_back(wire); - while (simplify(wire, true, false, false, 1, -1, false, false)) { } - - Yosys::AST::AstNode *lvalue = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - lvalue->str = wire->str; - - Yosys::AST::AstNode *always = new Yosys::AST::AstNode(Yosys::AST::AST_ALWAYS, new Yosys::AST::AstNode(Yosys::AST::AST_BLOCK, - new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, lvalue, ast_node->clone()))); - always->children[0]->children[0]->was_checked = true; - - current_ast_mod->children.push_back(always); - - goto replace_fcall_with_id; - } - - if (decl->attributes.count(ID::via_celltype)) - { - std::string celltype = decl->attributes.at(ID::via_celltype)->asAttrConst().decode_string(); - std::string outport = ast_node->str; - - if (celltype.find(' ') != std::string::npos) { - int pos = celltype.find(' '); - outport = RTLIL::escape_id(celltype.substr(pos+1)); - celltype = RTLIL::escape_id(celltype.substr(0, pos)); - } else - celltype = RTLIL::escape_id(celltype); - - Yosys::AST::AstNode *cell = new Yosys::AST::AstNode(Yosys::AST::AST_CELL, new Yosys::AST::AstNode(Yosys::AST::AST_CELLTYPE)); - cell->str = prefix.substr(0, GetSize(prefix)-1); - cell->children[0]->str = celltype; - - for (auto attr : decl->attributes) - if (attr.first.str().rfind("\\via_celltype_defparam_", 0) == 0) - { - Yosys::AST::AstNode *cell_arg = new Yosys::AST::AstNode(Yosys::AST::AST_PARASET, attr.second->clone()); - cell_arg->str = RTLIL::escape_id(attr.first.substr(strlen("\\via_celltype_defparam_"))); - cell->children.push_back(cell_arg); - } - - for (auto child : decl->children) - if (child->type == Yosys::AST::AST_WIRE && (child->is_input || child->is_output || (ast_node->type == Yosys::AST::AST_FCALL && child->str == ast_node->str))) - { - Yosys::AST::AstNode *wire = child->clone(); - wire->port_id = 0; - wire->is_input = false; - wire->is_output = false; - current_ast_mod->children.push_back(wire); - while (simplify(wire, true, false, false, 1, -1, false, false)) { } - - Yosys::AST::AstNode *wire_id = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - wire_id->str = wire->str; - - if ((child->is_input || child->is_output) && arg_count < ast_node->children.size()) - { - Yosys::AST::AstNode *arg = ast_node->children[arg_count++]->clone(); - Yosys::AST::AstNode *assign = child->is_input ? - new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, wire_id->clone(), arg) : - new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, arg, wire_id->clone()); - assign->children[0]->was_checked = true; - - for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { - if (*it != current_block_child) - continue; - current_block->children.insert(it, assign); - break; - } - } - - Yosys::AST::AstNode *cell_arg = new Yosys::AST::AstNode(Yosys::AST::AST_ARGUMENT, wire_id); - cell_arg->str = child->str == ast_node->str ? outport : child->str; - cell->children.push_back(cell_arg); - } - - current_ast_mod->children.push_back(cell); - goto replace_fcall_with_id; - } - - for (auto child : decl->children) - if (child->type == Yosys::AST::AST_WIRE || child->type == Yosys::AST::AST_MEMORY || child->type == Yosys::AST::AST_PARAMETER || child->type == Yosys::AST::AST_LOCALPARAM || child->type == Yosys::AST::AST_ENUM_ITEM) - { - Yosys::AST::AstNode *wire = nullptr; - - if (wire_cache.count(child->str)) - { - wire = wire_cache.at(child->str); - bool contains_value = wire->type == Yosys::AST::AST_LOCALPARAM; - if (wire->children.size() == contains_value) { - for (auto c : child->children) - wire->children.push_back(c->clone()); - } else if (!child->children.empty()) { - while (simplify(child, true, false, false, stage, -1, false, false)) { } - if (GetSize(child->children) == GetSize(wire->children) - contains_value) { - for (int i = 0; i < GetSize(child->children); i++) - if (*child->children.at(i) != *wire->children.at(i + contains_value)) - goto tcall_incompatible_wires; - } else { - tcall_incompatible_wires: - log_file_error(ast_node->filename, ast_node->location.first_line, "Incompatible re-declaration of wire %s.\n", child->str.c_str()); - } - } - } - else - { - wire = child->clone(); - wire->port_id = 0; - wire->is_input = false; - wire->is_output = false; - wire->is_reg = true; - wire->attributes[ID::nosync] = Yosys::AST::AstNode::mkconst_int(1, false); - if (child->type == Yosys::AST::AST_ENUM_ITEM) - wire->attributes[ID::enum_base_type] = child->attributes[ID::enum_base_type]; - - wire_cache[child->str] = wire; - - current_scope[wire->str] = wire; - current_ast_mod->children.push_back(wire); - } - - while (simplify(wire, true, false, false, 1, -1, false, false)) { } - - if ((child->is_input || child->is_output) && arg_count < ast_node->children.size()) - { - Yosys::AST::AstNode *arg = ast_node->children[arg_count++]->clone(); - // convert purely constant arguments into localparams - if (child->is_input && child->type == Yosys::AST::AST_WIRE && arg->type == Yosys::AST::AST_CONSTANT && node_contains_assignment_to(decl, child)) { - wire->type = Yosys::AST::AST_LOCALPARAM; - if (wire->attributes.count(ID::nosync)) - delete wire->attributes.at(ID::nosync); - wire->attributes.erase(ID::nosync); - wire->children.insert(wire->children.begin(), arg->clone()); - // args without a range implicitly have width 1 - if (wire->children.back()->type != Yosys::AST::AST_RANGE) { - // check if this wire is redeclared with an explicit size - bool uses_explicit_size = false; - for (const Yosys::AST::AstNode *other_child : decl->children) - if (other_child->type == Yosys::AST::AST_WIRE && child->str == other_child->str - && !other_child->children.empty() - && other_child->children.back()->type == Yosys::AST::AST_RANGE) { - uses_explicit_size = true; - break; - } - if (!uses_explicit_size) { - Yosys::AST::AstNode* range = new Yosys::AST::AstNode(); - range->type = Yosys::AST::AST_RANGE; - wire->children.push_back(range); - range->children.push_back(Yosys::AST::AstNode::mkconst_int(0, true)); - range->children.push_back(Yosys::AST::AstNode::mkconst_int(0, true)); - } - } - // updates the sizing - while (simplify(wire, true, false, false, 1, -1, false, false)) { } - delete arg; - continue; - } - Yosys::AST::AstNode *wire_id = new Yosys::AST::AstNode(Yosys::AST::AST_IDENTIFIER); - wire_id->str = wire->str; - Yosys::AST::AstNode *assign = child->is_input ? - new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, wire_id, arg) : - new Yosys::AST::AstNode(Yosys::AST::AST_ASSIGN_EQ, arg, wire_id); - assign->children[0]->was_checked = true; - if (child->is_input) - new_stmts.push_back(assign); - else - output_assignments.push_back(assign); - } - } - - for (auto child : decl->children) - if (child->type != Yosys::AST::AST_WIRE && child->type != Yosys::AST::AST_MEMORY && child->type != Yosys::AST::AST_PARAMETER && child->type != Yosys::AST::AST_LOCALPARAM) - new_stmts.push_back(child->clone()); - - new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); - - for (auto it = current_block->children.begin(); ; it++) { - log_assert(it != current_block->children.end()); - if (*it == current_block_child) { - current_block->children.insert(it, new_stmts.begin(), new_stmts.end()); - break; - } - } - - replace_fcall_with_id: - delete decl; - if (ast_node->type == Yosys::AST::AST_FCALL) { - ast_node->delete_children(); - ast_node->type = Yosys::AST::AST_IDENTIFIER; - ast_node->str = prefix_id(prefix, "$result"); - } - if (ast_node->type == Yosys::AST::AST_TCALL) - ast_node->str = ""; - did_something = true; - } - -replace_fcall_later:; - - // perform const folding when activated - if (const_fold) - { - bool string_op; - std::vector tmp_bits; - RTLIL::Const (*const_func)(const RTLIL::Const&, const RTLIL::Const&, bool, bool, ys_size_type); - RTLIL::Const dummy_arg; - - switch (ast_node->type) - { - case Yosys::AST::AST_IDENTIFIER: - if (current_scope.count(ast_node->str) > 0 && (current_scope[ast_node->str]->type == Yosys::AST::AST_PARAMETER || current_scope[ast_node->str]->type == Yosys::AST::AST_LOCALPARAM || current_scope[ast_node->str]->type == Yosys::AST::AST_ENUM_ITEM)) { - if (current_scope[ast_node->str]->children[0]->type == Yosys::AST::AST_CONSTANT) { - if (ast_node->children.size() != 0 && ast_node->children[0]->type == Yosys::AST::AST_RANGE && ast_node->children[0]->range_valid) { - std::vector data; - bool param_upto = current_scope[ast_node->str]->range_valid && current_scope[ast_node->str]->range_swapped; - int param_offset = current_scope[ast_node->str]->range_valid ? current_scope[ast_node->str]->range_right : 0; - int param_width = current_scope[ast_node->str]->range_valid ? current_scope[ast_node->str]->range_left - current_scope[ast_node->str]->range_right + 1 : - GetSize(current_scope[ast_node->str]->children[0]->bits); - int tmp_range_left = ast_node->children[0]->range_left, tmp_range_right = ast_node->children[0]->range_right; - if (param_upto) { - tmp_range_left = (param_width + 2*param_offset) - ast_node->children[0]->range_right - 1; - tmp_range_right = (param_width + 2*param_offset) - ast_node->children[0]->range_left - 1; - } - Yosys::AST::AstNode *member_node = systemverilog_plugin::get_struct_member(ast_node); - int chunk_offset = member_node ? member_node->range_right : 0; - log_assert(!(chunk_offset && param_upto)); - for (int i = tmp_range_right; i <= tmp_range_left; i++) { - int index = i - param_offset; - if (0 <= index && index < param_width) - data.push_back(current_scope[ast_node->str]->children[0]->bits[chunk_offset + index]); - else - data.push_back(RTLIL::State::Sx); - } - newNode = Yosys::AST::AstNode::mkconst_bits(data, false); - } else - if (ast_node->children.size() == 0) - newNode = current_scope[ast_node->str]->children[0]->clone(); - } else - if (current_scope[ast_node->str]->children[0]->isConst()) - newNode = current_scope[ast_node->str]->children[0]->clone(); - } - else if (at_zero && current_scope.count(ast_node->str) > 0) { - Yosys::AST::AstNode *node = current_scope[ast_node->str]; - if (node->type == Yosys::AST::AST_WIRE || node->type == Yosys::AST::AST_AUTOWIRE || node->type == Yosys::AST::AST_MEMORY) - newNode = node->mkconst_int(0, sign_hint, width_hint); - } - break; - case Yosys::AST::AST_MEMRD: - if (at_zero) { - newNode = Yosys::AST::AstNode::mkconst_int(0, sign_hint, width_hint); - } - break; - case Yosys::AST::AST_BIT_NOT: - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT) { - RTLIL::Const y = RTLIL::const_not(ast_node->children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint); - newNode = Yosys::AST::AstNode::mkconst_bits(y.bits, sign_hint); - } - break; - case Yosys::AST::AST_TO_SIGNED: - case Yosys::AST::AST_TO_UNSIGNED: - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT) { - RTLIL::Const y = ast_node->children[0]->bitsAsConst(width_hint, sign_hint); - newNode = Yosys::AST::AstNode::mkconst_bits(y.bits, ast_node->type == Yosys::AST::AST_TO_SIGNED); - } - break; - if (0) { case Yosys::AST::AST_BIT_AND: const_func = RTLIL::const_and; } - if (0) { case Yosys::AST::AST_BIT_OR: const_func = RTLIL::const_or; } - if (0) { case Yosys::AST::AST_BIT_XOR: const_func = RTLIL::const_xor; } - if (0) { case Yosys::AST::AST_BIT_XNOR: const_func = RTLIL::const_xnor; } - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT && ast_node->children[1]->type == Yosys::AST::AST_CONSTANT) { - RTLIL::Const y = const_func(ast_node->children[0]->bitsAsConst(width_hint, sign_hint), - ast_node->children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint); - newNode = Yosys::AST::AstNode::mkconst_bits(y.bits, sign_hint); - } - break; - if (0) { case Yosys::AST::AST_REDUCE_AND: const_func = RTLIL::const_reduce_and; } - if (0) { case Yosys::AST::AST_REDUCE_OR: const_func = RTLIL::const_reduce_or; } - if (0) { case Yosys::AST::AST_REDUCE_XOR: const_func = RTLIL::const_reduce_xor; } - if (0) { case Yosys::AST::AST_REDUCE_XNOR: const_func = RTLIL::const_reduce_xnor; } - if (0) { case Yosys::AST::AST_REDUCE_BOOL: const_func = RTLIL::const_reduce_bool; } - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT) { - RTLIL::Const y = const_func(RTLIL::Const(ast_node->children[0]->bits), dummy_arg, false, false, -1); - newNode = Yosys::AST::AstNode::mkconst_bits(y.bits, false); - } - break; - case Yosys::AST::AST_LOGIC_NOT: - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT) { - RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(ast_node->children[0]->bits), dummy_arg, ast_node->children[0]->is_signed, false, -1); - newNode = Yosys::AST::AstNode::mkconst_bits(y.bits, false); - } else - if (ast_node->children[0]->isConst()) { - newNode = Yosys::AST::AstNode::mkconst_int(ast_node->children[0]->asReal(sign_hint) == 0, false, 1); - } - break; - if (0) { case Yosys::AST::AST_LOGIC_AND: const_func = RTLIL::const_logic_and; } - if (0) { case Yosys::AST::AST_LOGIC_OR: const_func = RTLIL::const_logic_or; } - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT && ast_node->children[1]->type == Yosys::AST::AST_CONSTANT) { - RTLIL::Const y = const_func(RTLIL::Const(ast_node->children[0]->bits), RTLIL::Const(ast_node->children[1]->bits), - ast_node->children[0]->is_signed, ast_node->children[1]->is_signed, -1); - newNode = Yosys::AST::AstNode::mkconst_bits(y.bits, false); - } else - if (ast_node->children[0]->isConst() && ast_node->children[1]->isConst()) { - if (ast_node->type == Yosys::AST::AST_LOGIC_AND) - newNode = Yosys::AST::AstNode::mkconst_int((ast_node->children[0]->asReal(sign_hint) != 0) && (ast_node->children[1]->asReal(sign_hint) != 0), false, 1); - else - newNode = Yosys::AST::AstNode::mkconst_int((ast_node->children[0]->asReal(sign_hint) != 0) || (ast_node->children[1]->asReal(sign_hint) != 0), false, 1); - } - break; - if (0) { case Yosys::AST::AST_SHIFT_LEFT: const_func = RTLIL::const_shl; } - if (0) { case Yosys::AST::AST_SHIFT_RIGHT: const_func = RTLIL::const_shr; } - if (0) { case Yosys::AST::AST_SHIFT_SLEFT: const_func = RTLIL::const_sshl; } - if (0) { case Yosys::AST::AST_SHIFT_SRIGHT: const_func = RTLIL::const_sshr; } - if (0) { case Yosys::AST::AST_POW: const_func = RTLIL::const_pow; } - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT && ast_node->children[1]->type == Yosys::AST::AST_CONSTANT) { - RTLIL::Const y = const_func(ast_node->children[0]->bitsAsConst(width_hint, sign_hint), - RTLIL::Const(ast_node->children[1]->bits), sign_hint, ast_node->type == Yosys::AST::AST_POW ? ast_node->children[1]->is_signed : false, width_hint); - newNode = Yosys::AST::AstNode::mkconst_bits(y.bits, sign_hint); - } else - if (ast_node->type == Yosys::AST::AST_POW && ast_node->children[0]->isConst() && ast_node->children[1]->isConst()) { - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_REALVALUE); - newNode->realvalue = pow(ast_node->children[0]->asReal(sign_hint), ast_node->children[1]->asReal(sign_hint)); - } - break; - if (0) { case Yosys::AST::AST_LT: const_func = RTLIL::const_lt; } - if (0) { case Yosys::AST::AST_LE: const_func = RTLIL::const_le; } - if (0) { case Yosys::AST::AST_EQ: const_func = RTLIL::const_eq; } - if (0) { case Yosys::AST::AST_NE: const_func = RTLIL::const_ne; } - if (0) { case Yosys::AST::AST_EQX: const_func = RTLIL::const_eqx; } - if (0) { case Yosys::AST::AST_NEX: const_func = RTLIL::const_nex; } - if (0) { case Yosys::AST::AST_GE: const_func = RTLIL::const_ge; } - if (0) { case Yosys::AST::AST_GT: const_func = RTLIL::const_gt; } - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT && ast_node->children[1]->type == Yosys::AST::AST_CONSTANT) { - int cmp_width = max(ast_node->children[0]->bits.size(), ast_node->children[1]->bits.size()); - bool cmp_signed = ast_node->children[0]->is_signed && ast_node->children[1]->is_signed; - RTLIL::Const y = const_func(ast_node->children[0]->bitsAsConst(cmp_width, cmp_signed), - ast_node->children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1); - newNode = Yosys::AST::AstNode::mkconst_bits(y.bits, false); - } else - if (ast_node->children[0]->isConst() && ast_node->children[1]->isConst()) { - bool cmp_signed = (ast_node->children[0]->type == Yosys::AST::AST_REALVALUE || ast_node->children[0]->is_signed) && (ast_node->children[1]->type == Yosys::AST::AST_REALVALUE || ast_node->children[1]->is_signed); - switch (ast_node->type) { - case Yosys::AST::AST_LT: newNode = Yosys::AST::AstNode::mkconst_int(ast_node->children[0]->asReal(cmp_signed) < ast_node->children[1]->asReal(cmp_signed), false, 1); break; - case Yosys::AST::AST_LE: newNode = Yosys::AST::AstNode::mkconst_int(ast_node->children[0]->asReal(cmp_signed) <= ast_node->children[1]->asReal(cmp_signed), false, 1); break; - case Yosys::AST::AST_EQ: newNode = Yosys::AST::AstNode::mkconst_int(ast_node->children[0]->asReal(cmp_signed) == ast_node->children[1]->asReal(cmp_signed), false, 1); break; - case Yosys::AST::AST_NE: newNode = Yosys::AST::AstNode::mkconst_int(ast_node->children[0]->asReal(cmp_signed) != ast_node->children[1]->asReal(cmp_signed), false, 1); break; - case Yosys::AST::AST_EQX: newNode = Yosys::AST::AstNode::mkconst_int(ast_node->children[0]->asReal(cmp_signed) == ast_node->children[1]->asReal(cmp_signed), false, 1); break; - case Yosys::AST::AST_NEX: newNode = Yosys::AST::AstNode::mkconst_int(ast_node->children[0]->asReal(cmp_signed) != ast_node->children[1]->asReal(cmp_signed), false, 1); break; - case Yosys::AST::AST_GE: newNode = Yosys::AST::AstNode::mkconst_int(ast_node->children[0]->asReal(cmp_signed) >= ast_node->children[1]->asReal(cmp_signed), false, 1); break; - case Yosys::AST::AST_GT: newNode = Yosys::AST::AstNode::mkconst_int(ast_node->children[0]->asReal(cmp_signed) > ast_node->children[1]->asReal(cmp_signed), false, 1); break; - default: log_abort(); - } - } - break; - if (0) { case Yosys::AST::AST_ADD: const_func = RTLIL::const_add; } - if (0) { case Yosys::AST::AST_SUB: const_func = RTLIL::const_sub; } - if (0) { case Yosys::AST::AST_MUL: const_func = RTLIL::const_mul; } - if (0) { case Yosys::AST::AST_DIV: const_func = RTLIL::const_div; } - if (0) { case Yosys::AST::AST_MOD: const_func = RTLIL::const_mod; } - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT && ast_node->children[1]->type == Yosys::AST::AST_CONSTANT) { - RTLIL::Const y = const_func(ast_node->children[0]->bitsAsConst(width_hint, sign_hint), - ast_node->children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint); - newNode = Yosys::AST::AstNode::mkconst_bits(y.bits, sign_hint); - } else - if (ast_node->children[0]->isConst() && ast_node->children[1]->isConst()) { - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_REALVALUE); - switch (ast_node->type) { - case Yosys::AST::AST_ADD: newNode->realvalue = ast_node->children[0]->asReal(sign_hint) + ast_node->children[1]->asReal(sign_hint); break; - case Yosys::AST::AST_SUB: newNode->realvalue = ast_node->children[0]->asReal(sign_hint) - ast_node->children[1]->asReal(sign_hint); break; - case Yosys::AST::AST_MUL: newNode->realvalue = ast_node->children[0]->asReal(sign_hint) * ast_node->children[1]->asReal(sign_hint); break; - case Yosys::AST::AST_DIV: newNode->realvalue = ast_node->children[0]->asReal(sign_hint) / ast_node->children[1]->asReal(sign_hint); break; - case Yosys::AST::AST_MOD: newNode->realvalue = fmod(ast_node->children[0]->asReal(sign_hint), ast_node->children[1]->asReal(sign_hint)); break; - default: log_abort(); - } - } - break; - if (0) { case Yosys::AST::AST_SELFSZ: const_func = RTLIL::const_pos; } - if (0) { case Yosys::AST::AST_POS: const_func = RTLIL::const_pos; } - if (0) { case Yosys::AST::AST_NEG: const_func = RTLIL::const_neg; } - if (ast_node->children[0]->type == Yosys::AST::AST_CONSTANT) { - RTLIL::Const y = const_func(ast_node->children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint); - newNode = Yosys::AST::AstNode::mkconst_bits(y.bits, sign_hint); - } else - if (ast_node->children[0]->isConst()) { - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_REALVALUE); - if (ast_node->type == Yosys::AST::AST_NEG) - newNode->realvalue = -ast_node->children[0]->asReal(sign_hint); - else - newNode->realvalue = +ast_node->children[0]->asReal(sign_hint); - } - break; - case Yosys::AST::AST_TERNARY: - if (ast_node->children[0]->isConst()) - { - auto pair = ast_node->get_tern_choice(); - Yosys::AST::AstNode *choice = pair.first; - Yosys::AST::AstNode *not_choice = pair.second; - - if (choice != NULL) { - if (choice->type == Yosys::AST::AST_CONSTANT) { - int other_width_hint = width_hint; - bool other_sign_hint = sign_hint, other_real = false; - not_choice->detectSignWidth(other_width_hint, other_sign_hint, &other_real); - if (other_real) { - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_REALVALUE); - choice->detectSignWidth(width_hint, sign_hint); - newNode->realvalue = choice->asReal(sign_hint); - } else { - RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint); - if (choice->is_string && y.bits.size() % 8 == 0 && sign_hint == false) - newNode = Yosys::AST::AstNode::mkconst_str(y.bits); - else - newNode = Yosys::AST::AstNode::mkconst_bits(y.bits, sign_hint); - } - } else - if (choice->isConst()) { - newNode = choice->clone(); - } - } else if (ast_node->children[1]->type == Yosys::AST::AST_CONSTANT && ast_node->children[2]->type == Yosys::AST::AST_CONSTANT) { - RTLIL::Const a = ast_node->children[1]->bitsAsConst(width_hint, sign_hint); - RTLIL::Const b = ast_node->children[2]->bitsAsConst(width_hint, sign_hint); - log_assert(a.bits.size() == b.bits.size()); - for (size_t i = 0; i < a.bits.size(); i++) - if (a.bits[i] != b.bits[i]) - a.bits[i] = RTLIL::State::Sx; - newNode = Yosys::AST::AstNode::mkconst_bits(a.bits, sign_hint); - } else if (ast_node->children[1]->isConst() && ast_node->children[2]->isConst()) { - newNode = new Yosys::AST::AstNode(Yosys::AST::AST_REALVALUE); - if (ast_node->children[1]->asReal(sign_hint) == ast_node->children[2]->asReal(sign_hint)) - newNode->realvalue = ast_node->children[1]->asReal(sign_hint); - else - // IEEE Std 1800-2012 Sec. 11.4.11 states that the entry in Table 7-1 for - // the data type in question should be returned if the ?: is ambiguous. The - // value in Table 7-1 for the 'real' type is 0.0. - newNode->realvalue = 0.0; - } - } - break; - case Yosys::AST::AST_CAST_SIZE: - if (ast_node->children.at(0)->type == Yosys::AST::AST_CONSTANT && ast_node->children.at(1)->type == Yosys::AST::AST_CONSTANT) { - int width = ast_node->children[0]->bitsAsConst().as_int(); - RTLIL::Const val; - if (ast_node->children[1]->is_unsized) - val = ast_node->children[1]->bitsAsUnsizedConst(width); - else - val = ast_node->children[1]->bitsAsConst(width); - newNode = Yosys::AST::AstNode::mkconst_bits(val.bits, ast_node->children[1]->is_signed); - } - break; - case Yosys::AST::AST_CONCAT: - string_op = !ast_node->children.empty(); - for (auto it = ast_node->children.begin(); it != ast_node->children.end(); it++) { - if ((*it)->type != Yosys::AST::AST_CONSTANT) - goto not_const; - if (!(*it)->is_string) - string_op = false; - tmp_bits.insert(tmp_bits.end(), (*it)->bits.begin(), (*it)->bits.end()); - } - newNode = string_op ? Yosys::AST::AstNode::mkconst_str(tmp_bits) : Yosys::AST::AstNode::mkconst_bits(tmp_bits, false); - break; - case Yosys::AST::AST_REPLICATE: - if (ast_node->children.at(0)->type != Yosys::AST::AST_CONSTANT || ast_node->children.at(1)->type != Yosys::AST::AST_CONSTANT) - goto not_const; - for (int i = 0; i < ast_node->children[0]->bitsAsConst().as_int(); i++) - tmp_bits.insert(tmp_bits.end(), ast_node->children.at(1)->bits.begin(), ast_node->children.at(1)->bits.end()); - newNode = ast_node->children.at(1)->is_string ? Yosys::AST::AstNode::mkconst_str(tmp_bits) : Yosys::AST::AstNode::mkconst_bits(tmp_bits, false); - break; - default: - not_const: - break; - } - } - - // if any of the above set 'newNode' -> use 'newNode' as template to update 'ast_node' - if (newNode) { -apply_newNode: - // fprintf(stderr, "----\n"); - // dumpAst(stderr, "- "); - // newNode->dumpAst(stderr, "+ "); - log_assert(newNode != NULL); - newNode->filename = ast_node->filename; - newNode->location = ast_node->location; - newNode->cloneInto(ast_node); - delete newNode; - did_something = true; - } - - if (!did_something) - ast_node->basic_prep = true; - - recursion_counter--; - return did_something; -} - -} // namespace systemverilog_plugin diff --git a/systemverilog-plugin/third_party/yosys/simplify.h b/systemverilog-plugin/third_party/yosys/simplify.h deleted file mode 100644 index 69cd4f418..000000000 --- a/systemverilog-plugin/third_party/yosys/simplify.h +++ /dev/null @@ -1,7 +0,0 @@ -#include "frontends/ast/ast.h" - -namespace systemverilog_plugin -{ - using ys_size_type = int; // Makes it easy to change if changed upstream. - bool simplify(Yosys::AST::AstNode *ast_node, bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param); -} diff --git a/systemverilog-plugin/uhdmastfrontend.cc b/systemverilog-plugin/uhdmastfrontend.cc deleted file mode 100644 index 58bd36e75..000000000 --- a/systemverilog-plugin/uhdmastfrontend.cc +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 "uhdm/uhdm-version.h" // UHDM_VERSION define -#include "uhdm/vpi_visitor.h" // visit_object -#include "uhdmcommonfrontend.h" - -namespace systemverilog_plugin -{ - -using namespace ::Yosys; - -struct UhdmAstFrontend : public UhdmCommonFrontend { - UhdmAstFrontend() : UhdmCommonFrontend("uhdm", "read UHDM file") {} - void help() override - { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| - log("\n"); - log(" read_uhdm [options] [filename]\n"); - log("\n"); - log("Load design from a UHDM file into the current design\n"); - log("\n"); - this->print_read_options(); - } - AST::AstNode *parse(std::string filename) override - { - UHDM::Serializer serializer; - - std::vector restoredDesigns = serializer.Restore(filename); - UHDM::SynthSubset *synthSubset = - make_new_object_with_optional_extra_true_arg(&serializer, this->shared.nonSynthesizableObjects, false); - synthSubset->listenDesigns(restoredDesigns); - delete synthSubset; - UhdmAst uhdm_ast(this->shared); - AST::AstNode *current_ast = uhdm_ast.visit_designs(restoredDesigns); - for (auto design : restoredDesigns) - vpi_release_handle(design); - - serializer.Purge(); - return current_ast; - } - void call_log_header(RTLIL::Design *design) override { log_header(design, "Executing UHDM frontend.\n"); } -} UhdmAstFrontend; - -} // namespace systemverilog_plugin diff --git a/systemverilog-plugin/uhdmastshared.h b/systemverilog-plugin/uhdmastshared.h deleted file mode 100644 index 1dcd26011..000000000 --- a/systemverilog-plugin/uhdmastshared.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef _UHDM_AST_SHARED_H_ -#define _UHDM_AST_SHARED_H_ 1 - -#include "frontends/ast/ast.h" - -// Yosys defines a 'cover' macro in implicitly included kernel/log.h -// that clashes with cover-class in UHDM -#undef cover - -#include -#include -#include -#include - -namespace systemverilog_plugin -{ - -class UhdmAstShared -{ - private: - // Used for generating enum names - unsigned enum_count = 0; - - // Used for generating port IDS - unsigned port_count = 0; - - // Used for generating loop names - unsigned loop_count = 0; - - // Used for generating unique names for anonymous types used as parameters. - unsigned anonymous_type_count = 0; - - // Used to generate unique names for anonymous enum typedefs - unsigned anonymous_enum_typedef_count = 0; - - public: - ~UhdmAstShared() - { - for (const auto ¶m : param_types) - delete param.second; - param_types.clear(); - } - - // Generate the next enum ID (starting with 0) - unsigned next_enum_id() { return enum_count++; } - - // Generate the next port ID (starting with 1) - unsigned next_port_id() { return ++port_count; } - - // Generate the next loop ID (starting with 0) - unsigned next_loop_id() { return loop_count++; } - - // Generate the next anonymous type ID (starting with 0). - unsigned next_anonymous_type_id() { return anonymous_type_count++; } - - // Generate the next anonymous enum typedef ID (starting with 0). - unsigned next_anonymous_enum_typedef_id() { return anonymous_enum_typedef_count++; } - - // Flag that determines whether debug info should be printed - bool debug_flag = false; - - // Flag that determines whether we should ignore assert() statements - bool no_assert = false; - - // Flag that determines whether errors should be fatal - bool stop_on_error = true; - - // Flag that determines whether we should only parse the design - // applies only to read_systemverilog command - bool parse_only = false; - - // Flag that determines whether we should defer the elaboration - // applies only to read_systemverilog command - bool defer = false; - - // Flag that determines whether we should perform the elaboration now - // applies only to read_systemverilog command - bool link = false; - - // Flag equivalent to read_verilog -formal - // Defines FORMAL, undefines SYNTHESIS - // Allows verification constructs in Surelog - bool formal = false; - - // Top nodes of the design (modules, interfaces) - std::unordered_map top_nodes; - - // Map from AST param nodes to their types (used for params with struct types) - std::unordered_map param_types; - - ::Yosys::AST::AstNode *current_top_node = nullptr; - - // Currently processed UHDM module instance. - // Used as a fallback when obj->Instance() and obj->vpiParent() are not available. - const UHDM::any *current_instance = nullptr; - - // Set of non-synthesizable objects to skip in current design; - std::set nonSynthesizableObjects; - - // Map of anonymous enum types to generated typedefs - std::unordered_map> anonymous_enums; -}; - -} // namespace systemverilog_plugin - -#endif diff --git a/systemverilog-plugin/uhdmcommonfrontend.cc b/systemverilog-plugin/uhdmcommonfrontend.cc deleted file mode 100644 index da7ac31fa..000000000 --- a/systemverilog-plugin/uhdmcommonfrontend.cc +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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 "uhdmcommonfrontend.h" - -namespace systemverilog_plugin -{ - -using namespace ::Yosys; - -/* Stub for AST::process */ -static void set_line_num(int) {} - -/* Stub for AST::process */ -static int get_line_num(void) { return 1; } - -void UhdmCommonFrontend::print_read_options() -{ - log(" -noassert\n"); - log(" ignore assert() statements"); - log("\n"); - log(" -debug\n"); - log(" alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2\n"); - log("\n"); - log(" -dump_ast1\n"); - log(" dump abstract syntax tree (before simplification)\n"); - log("\n"); - log(" -dump_ast2\n"); - log(" dump abstract syntax tree (after simplification)\n"); - log("\n"); - log(" -no_dump_ptr\n"); - log(" do not include hex memory addresses in dump (easier to diff dumps)\n"); - log("\n"); - log(" -dump_vlog1\n"); - log(" dump ast as Verilog code (before simplification)\n"); - log("\n"); - log(" -dump_vlog2\n"); - log(" dump ast as Verilog code (after simplification)\n"); - log("\n"); - log(" -dump_rtlil\n"); - log(" dump generated RTLIL netlist\n"); - log("\n"); - log(" -defer\n"); - log(" only read the abstract syntax tree and defer actual compilation\n"); - log(" to a later 'hierarchy' command. Useful in cases where the default\n"); - log(" parameters of modules yield invalid or not synthesizable code.\n"); - log(" Needs to be followed by read_systemverilog -link after reading\n"); - log(" all files.\n"); - log("\n"); - log(" -link\n"); - log(" performs linking and elaboration of the files read with -defer\n"); - log("\n"); - log(" -parse-only\n"); - log(" this parameter only applies to read_systemverilog command,\n"); - log(" it runs only Surelog to parse design, but doesn't load generated\n"); - log(" tree into Yosys.\n"); - log("\n"); - log(" -formal\n"); - log(" enable support for SystemVerilog assertions and some Yosys extensions\n"); - log(" replace the implicit -D SYNTHESIS with -D FORMAL\n"); - log("\n"); -} - -void UhdmCommonFrontend::execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) -{ - this->call_log_header(design); - this->args = args; - - bool defer = false; - bool dump_ast1 = false; - bool dump_ast2 = false; - bool dump_vlog1 = false; - bool dump_vlog2 = false; - bool no_dump_ptr = false; - bool dump_rtlil = false; - std::vector unhandled_args; - - for (size_t i = 0; i < args.size(); i++) { - if (args[i] == "-debug") { - dump_ast1 = true; - dump_ast2 = true; - dump_vlog1 = true; - dump_vlog2 = true; - this->shared.debug_flag = true; - } else if (args[i] == "-noassert") { - this->shared.no_assert = true; - } else if (args[i] == "-defer") { - this->shared.defer = true; - } else if (args[i] == "-dump_ast1") { - dump_ast1 = true; - } else if (args[i] == "-dump_ast2") { - dump_ast2 = true; - } else if (args[i] == "-dump_vlog1") { - dump_vlog1 = true; - } else if (args[i] == "-dump_vlog2") { - dump_vlog2 = true; - } else if (args[i] == "-no_dump_ptr") { - no_dump_ptr = true; - } else if (args[i] == "-dump_rtlil") { - dump_rtlil = true; - } else if (args[i] == "-parse-only") { - this->shared.parse_only = true; - } else if (args[i] == "-link") { - this->shared.link = true; - // Surelog needs it in the command line to link correctly - unhandled_args.push_back(args[i]); - } else if (args[i] == "-formal") { - this->shared.formal = true; - // Surelog needs it in the command line to annotate UHDM - unhandled_args.push_back(args[i]); - } else { - unhandled_args.push_back(args[i]); - } - } - // Yosys gets confused when extra_args are passed with -link or no option - // It's done fully by Surelog, so skip it in this case - if (!this->shared.link) - extra_args(f, filename, args, args.size() - 1); - // pass only unhandled args to Surelog - // unhandled args starts with command name, - // but Surelog expects args[0] to be program name - // and skips it - this->args = unhandled_args; - - AST::current_filename = filename; - AST::set_line_num = &set_line_num; - AST::get_line_num = &get_line_num; - - bool dont_redefine = false; - bool default_nettype_wire = true; - - AST::AstNode *current_ast = parse(filename); - - if (current_ast) { - AST::process(design, current_ast, dump_ast1, dump_ast2, no_dump_ptr, dump_vlog1, dump_vlog2, dump_rtlil, false, false, false, false, false, - false, false, false, false, false, dont_redefine, false, defer, default_nettype_wire); - delete current_ast; - } -} - -} // namespace systemverilog_plugin diff --git a/systemverilog-plugin/uhdmcommonfrontend.h b/systemverilog-plugin/uhdmcommonfrontend.h deleted file mode 100644 index 324cadaa1..000000000 --- a/systemverilog-plugin/uhdmcommonfrontend.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 "uhdm/SynthSubset.h" -#include "uhdm/VpiListener.h" -#include -#include -#include - -namespace systemverilog_plugin -{ - -// FIXME (mglb): temporary fix to support UHDM both before and after the following change: -// https://github.com/chipsalliance/UHDM/commit/d78d094448bd94926644e48adea4df293b82f101 -// The commit introducing this code should to be reverted after Surelog is bumped to recent versions in all our repositories. -template , bool> = true> -static inline ObjT *make_new_object_with_optional_extra_true_arg(ArgN &&... arg_n) -{ - // Older UHDM version - return new ObjT(std::forward(arg_n)...); -} - -template , bool> = true> -static inline ObjT *make_new_object_with_optional_extra_true_arg(ArgN &&... arg_n) -{ - // Newer UHDM version - return new ObjT(std::forward(arg_n)..., true); -} - -struct UhdmCommonFrontend : public ::Yosys::Frontend { - UhdmAstShared shared; - std::vector args; - UhdmCommonFrontend(std::string name, std::string short_help) : Frontend(name, short_help) {} - virtual void print_read_options(); - virtual void help() = 0; - 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 args, ::Yosys::RTLIL::Design *design); -}; - -} // namespace systemverilog_plugin diff --git a/systemverilog-plugin/uhdmsurelogastfrontend.cc b/systemverilog-plugin/uhdmsurelogastfrontend.cc deleted file mode 100644 index 40d7a37a8..000000000 --- a/systemverilog-plugin/uhdmsurelogastfrontend.cc +++ /dev/null @@ -1,393 +0,0 @@ -/* - * 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 -#include -#else -#include -#include -#endif -#include - -#include - -#include "Surelog/API/Surelog.h" -#include "Surelog/CommandLine/CommandLineParser.h" -#include "Surelog/ErrorReporting/ErrorContainer.h" -#include "Surelog/SourceCompile/SymbolTable.h" -#include "uhdm/uhdm-version.h" // UHDM_VERSION define -#include "uhdm/vpi_visitor.h" // visit_object - -namespace systemverilog_plugin -{ - -using namespace ::Yosys; - -// Store systemverilog defaults to be passed for every invocation of read_systemverilog -static std::vector systemverilog_defaults; -static std::list> systemverilog_defaults_stack; - -// Store global definitions for top-level defines -static std::vector 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 &execute(std::unique_ptr errors, std::unique_ptr 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 errors = nullptr; - std::unique_ptr clp = nullptr; - SURELOG::scompiler *scompiler = nullptr; - std::vector 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 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(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(systemverilog_defaults[i].c_str())); - } - - // Add systemverilog defines args - for (size_t i = 0; i < systemverilog_defines.size(); ++i) - cstrings.push_back(const_cast(systemverilog_defines[i].c_str())); - } - - auto symbolTable = std::make_unique(); - auto errors = std::make_unique(symbolTable.get()); - auto clp = std::make_unique(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); - clp->setReportNonSynthesizable(true); - if (this->shared.defer) { - clp->setCompile(false); - clp->setElaborate(false); - clp->setSepComp(true); - } else { - clp->setCompile(true); - clp->setElaborate(true); - clp->setElabUhdm(true); - } - if (this->shared.link) { - clp->setLink(true); - } - - Compiler compiler; - const auto &uhdm_designs = compiler.execute(std::move(errors), std::move(clp)); - - // 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(&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); - - // 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 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 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 diff --git a/systemverilog-plugin/utils/memory.h b/systemverilog-plugin/utils/memory.h deleted file mode 100644 index 6cd43c0ff..000000000 --- a/systemverilog-plugin/utils/memory.h +++ /dev/null @@ -1,195 +0,0 @@ -#ifndef SYSTEMVERILOG_PLUGIN_UTILS_MEMORY_H_ -#define SYSTEMVERILOG_PLUGIN_UTILS_MEMORY_H_ - -#include -#include - -namespace systemverilog_plugin -{ - -// `std::default_delete` equivalent for any range of pointers, e.g. `std::vector`. -template struct default_delete_ptr_range { - void operator()(Range &range) const - { - for (auto *ptr : range) - delete ptr; - } -}; - -// Functor that takes a reference and does nothing. Useful as no-op deleter. -struct noop_delete { - template void operator()(T &) const {} -}; - -namespace utils_memory_internal -{ - -// Unique type for detecting invalid (missing) default_resource_deleter. -struct missing_type { -}; - -// Provider of default deleter functor for resource of type `R` used by `unique_resource`. -template struct default_resource_deleter { - using type = missing_type; -}; - -// Type trait for detecting whether type `R` is any range of pointers. -template ()))>> -using is_range_of_pointers_t = std::enable_if_t && !std::is_array_v>; - -// Overload for any range of pointers. -template struct default_resource_deleter> { - using type = default_delete_ptr_range; -}; - -// Convenience alias. -template using default_resource_deleter_t = typename default_resource_deleter::type; - -// Type trait for checking whether type `R` is a valid deleter of type `D`. -template struct is_valid_resource_deleter : std::false_type { -}; -template struct is_valid_resource_deleter()(std::declval()))>> : std::true_type { -}; - -// Convenience alias. -template inline constexpr bool is_valid_resource_deleter_v = is_valid_resource_deleter::value; - -} // namespace utils_memory_internal - -// Wrapper that holds and manages resource of type `Resource`. Equivalent of `unique_ptr` for non-pointer types. -// -// `unique_resource` tracks initialization status of its resource. The `Destructor` is called only when the resource is in initialized state. -// `unique_resource` constructed using default constructor is in uninitialized state. It becomes initialized when a valid resource is moved into it. -// Moving resource out or releasing it switches state to uninitialized. -// -// The API is based on unique_ptr rather than `unique_resource` from Library Fundamentals TS3. -template > class unique_resource -{ - // Check for errors in template parameters. - // Use of intermediate constexprs results in nicer error messages. - static constexpr bool deleter_for_resource_exists = !std::is_same_v; - static_assert(deleter_for_resource_exists, "'Deleter' has not been specified and no default deleter exists for type 'Resource'."); - static constexpr bool deleter_is_callable_with_resource_ref = utils_memory_internal::is_valid_resource_deleter_v; - static_assert(deleter_is_callable_with_resource_ref, "Object of type 'Deleter' must be callable with argument of type 'Resource &'."); - static constexpr bool resource_type_is_not_cvref = std::is_same_v>>; - static_assert(resource_type_is_not_cvref, "'Resource' must not be a reference or have const or volatile qualifier."); - - // Data members. - - Resource resource = {}; - bool initialized = false; - - public: - // Initialize - - unique_resource() = default; - - template unique_resource(OtherResource &&other) : resource(std::forward(other)), initialized(true) {} - - // Copy - - unique_resource(const unique_resource &) = delete; - - unique_resource &operator=(const unique_resource &) = delete; - - // Move - - template - unique_resource(unique_resource &&other) : resource(std::move(other.resource)), initialized(other.initialized) - { - other.initialized = false; - } - - template unique_resource &operator=(unique_resource &&other) - { - resource = std::move(other.resource); - initialized = other.initialized; - other.initialized = false; - }; - - // Destroy - - ~unique_resource() - { - if (initialized) { - Deleter{}(resource); - } - } - - // Data access - - Resource &get() - { - assert(initialized); - return resource; - } - - const Resource &get() const - { - assert(initialized); - return resource; - } - - Resource &operator*() - { - assert(initialized); - return resource; - } - - const Resource &operator*() const - { - assert(initialized); - return resource; - } - - Resource *operator->() - { - assert(initialized); - return &resource; - } - - const Resource *operator->() const - { - assert(initialized); - return &resource; - } - - operator bool() const { return initialized; } - - // Operations - - Resource release() - { - Resource r = std::move(resource); - initialized = false; - return r; - } - - void reset() - { - if (initialized) { - Deleter{}(resource); - initialized = false; - } - } - - template void reset(OtherResource &&other) - { - if (initialized) { - Deleter{}(resource); - } - resource = std::forward(other); - initialized = true; - } -}; - -// Creates `unique_resource` and initializes it with a resource constructed using specified arguments. -template , class... Tn> -inline unique_resource make_unique_resource(Tn &&... arg_n) -{ - return unique_resource(Resource(std::forward(arg_n)...)); -} - -} // namespace systemverilog_plugin - -#endif // SYSTEMVERILOG_PLUGIN_UTILS_MEMORY_H_ diff --git a/uhdm-plugin/Makefile b/uhdm-plugin/Makefile deleted file mode 100644 index 4f6783059..000000000 --- a/uhdm-plugin/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# 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 - -PLUGIN_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) - -NAME = uhdm -SOURCES = uhdm.cc -include ../Makefile_plugin.common diff --git a/uhdm-plugin/README.md b/uhdm-plugin/README.md deleted file mode 100644 index cecd42706..000000000 --- a/uhdm-plugin/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# UHDM Plugin - -The UHDM plugin has been renamed to [SystemVerilog](../systemverilog-plugin/). - -It is kept here for backwards compatibility reasons. -When loaded, it shows a warning and loads the `SystemVerilog` plugin (if available). diff --git a/uhdm-plugin/tests/Makefile b/uhdm-plugin/tests/Makefile deleted file mode 100644 index 1c6f1d813..000000000 --- a/uhdm-plugin/tests/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# 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 $(shell pwd)/../../Makefile_test.common diff --git a/uhdm-plugin/uhdm.cc b/uhdm-plugin/uhdm.cc deleted file mode 100644 index 9fde149ee..000000000 --- a/uhdm-plugin/uhdm.cc +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 "kernel/log.h" - -USING_YOSYS_NAMESPACE - -PRIVATE_NAMESPACE_BEGIN - -struct UhdmDummy { - UhdmDummy() - { - log("\n"); - log("!! DEPRECATION WARNING !!\n"); - log("\n"); - log("The uhdm plugin has been renamed to systemverilog.\n"); - log("Loading the systemverilog plugin...\n"); - - std::vector plugin_aliases; - load_plugin("systemverilog", plugin_aliases); - } -} UhdmDummy; - -PRIVATE_NAMESPACE_END