Remove the SV plugin
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
diff --git a/.github/workflows/build-and-test.sh b/.github/workflows/build-and-test.sh
index 69fd8a7..efab5db 100755
--- a/.github/workflows/build-and-test.sh
+++ b/.github/workflows/build-and-test.sh
@@ -28,13 +28,13 @@
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 95737de..f2fae72 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,8 +35,6 @@
- design_introspection
- integrateinv
- ql-qlf
- - systemverilog
- - uhdm
- dsp-ff
steps:
diff --git a/.github/workflows/setup.sh b/.github/workflows/setup.sh
index 38bcf3f..1111aa3 100644
--- a/.github/workflows/setup.sh
+++ b/.github/workflows/setup.sh
@@ -47,9 +47,9 @@
#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 5051171..11721f9 100644
--- a/Makefile
+++ b/Makefile
@@ -24,7 +24,7 @@
# TODO: pass as -D to gcc so that modules can provide e.g. --version flags.
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 @@
.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 53d80e5..d440eae 100644
--- a/README.md
+++ b/README.md
@@ -99,15 +99,7 @@
## 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 <command_name>` 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 a4cf46c..5e5062b 100644
--- a/environment.yml
+++ b/environment.yml
@@ -20,5 +20,4 @@
- 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 525d43a..0000000
--- 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 cdd2edd..c67fd2e 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 <command_name>` 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 45240c2..0000000
--- a/systemverilog-plugin/UhdmAst.cc
+++ /dev/null
@@ -1,5373 +0,0 @@
-#include <algorithm>
-#include <cstdlib>
-#include <cstring>
-#include <functional>
-#include <iostream>
-#include <limits>
-#include <regex>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "UhdmAst.h"
-#include "frontends/ast/ast.h"
-#include "libs/sha1/sha1.h"
-
-#include "utils/memory.h"
-
-// UHDM
-#include <uhdm/ExprEval.h>
-#include <uhdm/uhdm.h>
-#include <uhdm/vpi_user.h>
-
-#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 <typename T> 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 <typename T> ScopedValueChanger(T &)->ScopedValueChanger<T>;
-
-template <typename T> ScopedValueChanger(T &, const T &)->ScopedValueChanger<T>;
-
-// 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<int> &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<void(AST::AstNode *)> &f)
-{
- for (auto child : node->children) {
- f(child);
- visitEachDescendant(child, f);
- }
-}
-
-static void add_multirange_wire(AST::AstNode *node, std::vector<AST::AstNode *> packed_ranges, std::vector<AST::AstNode *> 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<size_t, size_t> set_multirange_dimensions(AST::AstNode *wire_node, const std::vector<AST::AstNode *> packed_ranges,
- const std::vector<AST::AstNode *> 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<AST::AstNode *> &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<int> 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<int>(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<std::vector<AST::AstNode *>> packed_ranges = wire_node->attributes.count(attr_id::packed_ranges)
- ? std::move(wire_node->attributes[attr_id::packed_ranges]->children)
- : std::vector<AST::AstNode *>{};
- delete_attribute(wire_node, attr_id::packed_ranges);
- unique_resource<std::vector<AST::AstNode *>> unpacked_ranges = wire_node->attributes.count(attr_id::unpacked_ranges)
- ? std::move(wire_node->attributes[attr_id::unpacked_ranges]->children)
- : std::vector<AST::AstNode *>{};
- 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<AST::AstNode *> packed_ranges_wiretype;
- std::vector<AST::AstNode *> 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<std::string, AST::AstNode *> &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<std::string, AST::AstNode *> memories;
- check_memories(node, "", memories);
-}
-
-static void warn_start_range(const std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges = wire_node->attributes.count(UhdmAst::packed_ranges())
- ? wire_node->attributes[UhdmAst::packed_ranges()]->children
- : std::vector<AST::AstNode *>();
- const std::vector<AST::AstNode *> unpacked_ranges = wire_node->attributes.count(UhdmAst::unpacked_ranges())
- ? wire_node->attributes[UhdmAst::unpacked_ranges()]->children
- : std::vector<AST::AstNode *>();
- 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<AST::AstNode *> 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<AST::AstNode *> struct_ranges;
-
- for (auto c : search_node->children) {
- if (c->type == static_cast<int>(AST::Extended::AST_DOT)) {
- // There should be only 1 AST_DOT node children
- log_assert(!sub_dot);
- sub_dot = expand_dot(current_struct_elem, c);
- }
- 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<AST::AstNode *> 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<std::string, AST::AstNode *> 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<AST::AstNode *> 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<int>(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<int>(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<int> child_node_types, vpiHandle parent_handle, const std::function<void(AST::AstNode *)> &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<int> child_node_types, vpiHandle parent_handle, const std::function<void(AST::AstNode *)> &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<void(AST::AstNode *)> &f)
-{
- std::vector<AST::AstNode *> 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<std::uint32_t>::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<std::int32_t>::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<bool(AST::AstNode *)> 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<int>((*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<AST::AstNode>(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<AST::AstNode>(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<AST::AstNode *> 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<AST::AstNode *> packed_ranges;
- std::vector<AST::AstNode *> 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<AST::AstNode *> 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<AST::AstNodeType> &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<AST::AstNode *>(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<std::pair<RTLIL::IdString, RTLIL::Const>> parameters;
-
- auto parameter_typedefs = make_unique_resource<std::vector<AST::AstNode *>>();
-
- 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<AST::AstNode *> packed_ranges;
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges;
- std::vector<AST::AstNode *> 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<const UHDM::logic_typespec *>();
- std::vector<UHDM::range *> 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<AST::AstNode *> packed_ranges;
- packed_ranges.push_back(make_range(31, 0));
- add_multirange_wire(current_node, std::move(packed_ranges), {});
- current_node->is_signed = true;
- }
- // We have to restore node's range_* properties if there's no range.
- const auto range_left = current_node->range_left;
- const auto range_right = current_node->range_right;
- const auto range_valid = current_node->range_valid;
- // Create a range from the typespec just for the purpose of copying it to consts.
- convert_packed_unpacked_range(current_node);
- const auto range_it = std::find_if(current_node->children.cbegin(), current_node->children.cend(),
- [](const AST::AstNode *n) { return n->type == AST::AST_RANGE || n->type == AST::AST_MULTIRANGE; });
- const auto *const range = range_it != current_node->children.cend() ? *range_it : nullptr;
- if (range) {
- current_node->children.erase(range_it);
- } else {
- current_node->range_left = range_left;
- current_node->range_right = range_right;
- current_node->range_valid = range_valid;
- }
-
- visit_one_to_one({vpiTypedefAlias}, obj_h, [&](AST::AstNode *node) {
- if (node) {
- current_node->attributes["\\enum_base_type"] = node;
- }
- });
- 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<AST::AstNode *> packed_ranges;
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges;
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges;
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges;
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges; // comes before wire name
- std::vector<AST::AstNode *> 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<AST::AstNode *> 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<size_t, AST::AstNode *> 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::AstNodeType>(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<AST::AstNode *> 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::AstNodeType>(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::AstNodeType>(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<AST::AstNode *> packed_ranges; // comes before wire name
- std::vector<AST::AstNode *> 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<const UHDM::func_call *>();
- 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<AST::AstNode *> packed_ranges; // comes before wire name
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges; // comes before wire name
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges; // comes before wire name
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges; // comes before wire name
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges; // comes before wire name
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges; // comes before wire name
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges; // comes before wire name
- std::vector<AST::AstNode *> 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<AST::AstNode *> packed_ranges; // comes before wire name
- std::vector<AST::AstNode *> 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::AstNodeType>(AST::Extended::AST_BREAK));
- break;
- case vpiContinue:
- // Will be resolved later by loop processor
- current_node = make_ast_node(static_cast<AST::AstNodeType>(AST::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<vpiHandle> &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 d60e827..0000000
--- 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 <vector>
-#undef cover
-
-#include "uhdmastshared.h"
-#include <memory>
-#include <uhdm/uhdm.h>
-
-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<int> child_node_types, vpiHandle parent_handle, const std::function<void(::Yosys::AST::AstNode *)> &f);
-
- // Walks through one-to-one relationships from given parent
- // node through the VPI interface, visiting child nodes belonging to
- // ChildrenNodeTypes that are present in the given object.
- void visit_one_to_one(const std::vector<int> child_node_types, vpiHandle parent_handle, const std::function<void(::Yosys::AST::AstNode *)> &f);
-
- // Visit children of type vpiRange that belong to the given parent node.
- void visit_range(vpiHandle obj_h, const std::function<void(::Yosys::AST::AstNode *)> &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<std::string, std::string> 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<vpiHandle> &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<AstNode>`.
-//
-// 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<AST::AstNode>(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<AstNode> node;
-
- public:
- explicit AstNodeBuilder(AstNodeType node_type) : node(new AstNode(node_type)) {}
- explicit AstNodeBuilder(std::unique_ptr<AstNode> 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<AstNode *> 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<AstNode>() { 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 3caff14..0000000
--- 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 74a27c2..0000000
--- 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 d496367..0000000
--- 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 d06d60b..0000000
--- 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 28a4447..0000000
--- 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 3dabd7e..0000000
--- 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 565946b..0000000
--- 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 b0362fc..0000000
--- 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 3b3a568..0000000
--- 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 5bd294a..0000000
--- 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 804d2e5..0000000
--- 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 f565a77..0000000
--- 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 14b37ad..0000000
--- 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 528e9f1..0000000
--- 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 b2590d3..0000000
--- 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 04e346c..0000000
--- 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 565946b..0000000
--- 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 b0362fc..0000000
--- 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 46b029a..0000000
--- 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 5bd294a..0000000
--- 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 d836d1e..0000000
--- 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 3ca8840..0000000
--- 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 df69e69..0000000
--- 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 96ea6cc..0000000
--- 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 <claire@yosyshq.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * ---
- *
- * The Verilog frontend.
- *
- * This frontend is using the AST frontend library (see frontends/ast/).
- * Thus this frontend does not generate RTLIL code directly but creates an
- * AST directly from the Verilog parse tree and then passes this AST to
- * the AST frontend library.
- *
- * ---
- *
- * This file contains an ad-hoc parser for Verilog constants. The Verilog
- * lexer does only recognize a constant but does not actually split it to its
- * components. I.e. it just passes the Verilog code for the constant to the
- * bison parser. The parser then uses the function const2ast() from this file
- * to create an AST node for the constant.
- *
- * ---
- *
- * The file has been adapted for use in Yosys SystemVerilog Plugin.
- *
- */
-
-#include "const2ast.h"
-#include "frontends/ast/ast.h"
-#include "kernel/log.h"
-
-#include <string>
-#include <cmath>
-#include <vector>
-
-using namespace Yosys;
-using namespace Yosys::AST;
-
-// divide an arbitrary length decimal number by two and return the rest
-static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
-{
- int carry = 0;
- for (size_t i = 0; i < digits.size(); i++) {
- if (digits[i] >= 10)
- log_file_error(current_filename, get_line_num(), "Invalid use of [a-fxz?] in decimal constant.\n");
- digits[i] += carry * 10;
- carry = digits[i] % 2;
- digits[i] /= 2;
- }
- while (!digits.empty() && !digits.front())
- digits.erase(digits.begin());
- return carry;
-}
-
-// find the number of significant bits in a binary number (not including the sign bit)
-static int my_ilog2(int x)
-{
- int ret = 0;
- while (x != 0 && x != -1) {
- x = x >> 1;
- ret++;
- }
- return ret;
-}
-
-// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
-static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized)
-{
- // all digits in string (MSB at index 0)
- std::vector<uint8_t> digits;
-
- while (*str) {
- if ('0' <= *str && *str <= '9')
- digits.push_back(*str - '0');
- else if ('a' <= *str && *str <= 'f')
- digits.push_back(10 + *str - 'a');
- else if ('A' <= *str && *str <= 'F')
- digits.push_back(10 + *str - 'A');
- else if (*str == 'x' || *str == 'X')
- digits.push_back(0xf0);
- else if (*str == 'z' || *str == 'Z' || *str == '?')
- digits.push_back(0xf1);
- str++;
- }
-
- if (base == 10 && GetSize(digits) == 1 && digits.front() >= 0xf0)
- base = 2;
-
- data.clear();
-
- if (base == 10) {
- while (!digits.empty())
- data.push_back(my_decimal_div_by_two(digits) ? State::S1 : State::S0);
- } else {
- int bits_per_digit = my_ilog2(base-1);
- for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) {
- if (*it > (base-1) && *it < 0xf0)
- log_file_error(current_filename, get_line_num(), "Digit larger than %d used in in base-%d constant.\n",
- base-1, base);
- for (int i = 0; i < bits_per_digit; i++) {
- int bitmask = 1 << i;
- if (*it == 0xf0)
- data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx);
- else if (*it == 0xf1)
- data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz);
- else
- data.push_back((*it & bitmask) ? State::S1 : State::S0);
- }
- }
- }
-
- int len = GetSize(data);
- RTLIL::State msb = data.empty() ? State::S0 : data.back();
-
- if (len_in_bits < 0) {
- if (len < 32)
- data.resize(32, msb == State::S0 || msb == State::S1 ? RTLIL::S0 : msb);
- return;
- }
-
- if (is_unsized && (len > len_in_bits))
- log_file_error(current_filename, get_line_num(), "Unsized constant must have width of 1 bit, but have %d bits!\n", len);
-
- for (len = len - 1; len >= 0; len--)
- if (data[len] == State::S1)
- break;
- if (msb == State::S0 || msb == State::S1) {
- len += 1;
- data.resize(len_in_bits, State::S0);
- } else {
- len += 2;
- data.resize(len_in_bits, msb);
- }
-
- if (len_in_bits == 0)
- log_file_error(current_filename, get_line_num(), "Illegal integer constant size of zero (IEEE 1800-2012, 5.7).\n");
-
- if (len > len_in_bits)
- log_warning("Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n",
- len_in_bits, len, current_filename.c_str(), get_line_num());
-}
-
-// convert the Verilog code for a constant to an AST node
-AstNode *systemverilog_plugin::const2ast(std::string code, char case_type, bool warn_z)
-{
- if (warn_z) {
- AstNode *ret = const2ast(code, case_type);
- if (ret != nullptr && std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end())
- log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n",
- current_filename.c_str(), get_line_num());
- return ret;
- }
-
- const char *str = code.c_str();
-
- // Strings
- if (*str == '"') {
- int len = strlen(str) - 2;
- std::vector<RTLIL::State> data;
- data.reserve(len * 8);
- for (int i = 0; i < len; i++) {
- unsigned char ch = str[len - i];
- for (int j = 0; j < 8; j++) {
- data.push_back((ch & 1) ? State::S1 : State::S0);
- ch = ch >> 1;
- }
- }
- AstNode *ast = AstNode::mkconst_bits(data, false);
- ast->str = code;
- return ast;
- }
-
- for (size_t i = 0; i < code.size(); i++)
- if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n')
- code.erase(code.begin()+(i--));
- str = code.c_str();
-
- char *endptr;
- long len_in_bits = strtol(str, &endptr, 10);
-
- // Simple base-10 integer
- if (*endptr == 0) {
- std::vector<RTLIL::State> data;
- my_strtobin(data, str, -1, 10, case_type, false);
- if (data.back() == State::S1)
- data.push_back(State::S0);
- return AstNode::mkconst_bits(data, true);
- }
-
- // unsized constant
- if (str == endptr)
- len_in_bits = -1;
-
- // The "<bits>'[sS]?[bodhBODH]<digits>" syntax
- if (*endptr == '\'')
- {
- std::vector<RTLIL::State> data;
- bool is_signed = false;
- bool is_unsized = len_in_bits < 0;
- if (*(endptr+1) == 's' || *(endptr+1) == 'S') {
- is_signed = true;
- endptr++;
- }
- switch (*(endptr+1))
- {
- case 'b':
- case 'B':
- my_strtobin(data, endptr+2, len_in_bits, 2, case_type, is_unsized);
- break;
- case 'o':
- case 'O':
- my_strtobin(data, endptr+2, len_in_bits, 8, case_type, is_unsized);
- break;
- case 'd':
- case 'D':
- my_strtobin(data, endptr+2, len_in_bits, 10, case_type, is_unsized);
- break;
- case 'h':
- case 'H':
- my_strtobin(data, endptr+2, len_in_bits, 16, case_type, is_unsized);
- break;
- default:
- char next_char = char(tolower(*(endptr+1)));
- if (next_char == '0' || next_char == '1' || next_char == 'x' || next_char == 'z') {
- is_unsized = true;
- my_strtobin(data, endptr+1, 1, 2, case_type, is_unsized);
- } else {
- return NULL;
- }
- }
- if (len_in_bits < 0) {
- if (is_signed && data.back() == State::S1)
- data.push_back(State::S0);
- }
- return AstNode::mkconst_bits(data, is_signed, is_unsized);
- }
-
- return NULL;
-}
diff --git a/systemverilog-plugin/third_party/yosys/const2ast.h b/systemverilog-plugin/third_party/yosys/const2ast.h
deleted file mode 100644
index f7130a2..0000000
--- 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 <string>
-
-namespace systemverilog_plugin
-{
- // this function converts a Verilog constant to an AST_CONSTANT node
- Yosys::AST::AstNode *const2ast(std::string code, char case_type = 0, bool warn_z = false);
-}
-
-#endif // SYSTEMVERILOG_PLUGIN_CONST2AST_H
diff --git a/systemverilog-plugin/third_party/yosys/simplify.cc b/systemverilog-plugin/third_party/yosys/simplify.cc
deleted file mode 100644
index cdc39b4..0000000
--- 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 <claire@yosyshq.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * ---
- *
- * 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 <sstream>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <math.h>
-
-#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<Yosys::AST::AstNode *> 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<RTLIL::IdString> 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<unsigned char>(c) < 33 || static_cast<unsigned char>(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<Yosys::AST::AstNode*, pool<std::string>> mem2reg_places;
- dict<Yosys::AST::AstNode*, uint32_t> 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<Yosys::AST::AstNode*> 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<Yosys::AST::AstNode*> 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<std::string, Yosys::AST::AstNode*> 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<std::string> existing;
- int counter = 0;
- ast_node->label_genblks(existing, counter);
- std::map<std::string, Yosys::AST::AstNode*> 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<Yosys::AST::AstNode*> 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<std::string, pool<int>> backup_memwr_visible;
- dict<std::string, pool<int>> 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<Yosys::AST::AstNode*> 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<Yosys::AST::AstNode*> 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<RTLIL::State> 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<RTLIL::State>(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<RTLIL::State>(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<RTLIL::State> 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<RTLIL::State> 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<RTLIL::State> 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<RTLIL::State> 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<RTLIL::State> 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<std::string> argtypes;
- std::vector<Yosys::AST::AstNode*> 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<Yosys::AST::AstNode*> queue;
- log_assert(current_always->children[0]->type == Yosys::AST::AST_BLOCK);
- queue.insert(current_always->children[0]);
- while (!unconditional_init && !queue.empty()) {
- pool<Yosys::AST::AstNode*> 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<std::string, Yosys::AST::AstNode*> wire_cache;
- vector<Yosys::AST::AstNode*> new_stmts;
- vector<Yosys::AST::AstNode*> 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<RTLIL::State> 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<RTLIL::State> 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 69cd4f4..0000000
--- 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 58bd36e..0000000
--- 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<vpiHandle> restoredDesigns = serializer.Restore(filename);
- UHDM::SynthSubset *synthSubset =
- make_new_object_with_optional_extra_true_arg<UHDM::SynthSubset>(&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 1dcd260..0000000
--- 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 <string>
-#include <uhdm/uhdm.h>
-#include <uhdm/vpi_user.h>
-#include <unordered_map>
-
-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<std::string, ::Yosys::AST::AstNode *> top_nodes;
-
- // Map from AST param nodes to their types (used for params with struct types)
- std::unordered_map<std::string, ::Yosys::AST::AstNode *> 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<const UHDM::BaseClass *> nonSynthesizableObjects;
-
- // Map of anonymous enum types to generated typedefs
- std::unordered_map<std::string, std::unordered_map<const UHDM::enum_typespec *, std::string>> anonymous_enums;
-};
-
-} // namespace systemverilog_plugin
-
-#endif
diff --git a/systemverilog-plugin/uhdmcommonfrontend.cc b/systemverilog-plugin/uhdmcommonfrontend.cc
deleted file mode 100644
index da7ac31..0000000
--- 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<std::string> 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<std::string> 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 324cada..0000000
--- 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 <string>
-#include <type_traits>
-#include <vector>
-
-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 <typename ObjT, typename... ArgN, std::enable_if_t<std::is_constructible_v<ObjT, ArgN...>, bool> = true>
-static inline ObjT *make_new_object_with_optional_extra_true_arg(ArgN &&... arg_n)
-{
- // Older UHDM version
- return new ObjT(std::forward<ArgN>(arg_n)...);
-}
-
-template <typename ObjT, typename... ArgN, std::enable_if_t<!std::is_constructible_v<ObjT, ArgN...>, bool> = true>
-static inline ObjT *make_new_object_with_optional_extra_true_arg(ArgN &&... arg_n)
-{
- // Newer UHDM version
- return new ObjT(std::forward<ArgN>(arg_n)..., true);
-}
-
-struct UhdmCommonFrontend : public ::Yosys::Frontend {
- UhdmAstShared shared;
- std::vector<std::string> args;
- UhdmCommonFrontend(std::string name, std::string short_help) : Frontend(name, short_help) {}
- virtual void print_read_options();
- virtual void help() = 0;
- virtual ::Yosys::AST::AstNode *parse(std::string filename) = 0;
- virtual void call_log_header(::Yosys::RTLIL::Design *design) = 0;
- void execute(std::istream *&f, std::string filename, std::vector<std::string> args, ::Yosys::RTLIL::Design *design);
-};
-
-} // namespace systemverilog_plugin
diff --git a/systemverilog-plugin/uhdmsurelogastfrontend.cc b/systemverilog-plugin/uhdmsurelogastfrontend.cc
deleted file mode 100644
index 40d7a37..0000000
--- 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 <direct.h>
-#include <process.h>
-#else
-#include <sys/param.h>
-#include <unistd.h>
-#endif
-#include <memory>
-
-#include <list>
-
-#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<std::string> systemverilog_defaults;
-static std::list<std::vector<std::string>> systemverilog_defaults_stack;
-
-// Store global definitions for top-level defines
-static std::vector<std::string> systemverilog_defines;
-
-// SURELOG::scompiler wrapper.
-// Owns UHDM/VPI resources used by designs returned from `execute`
-class Compiler
-{
- public:
- Compiler() = default;
- ~Compiler()
- {
- if (this->scompiler) {
- SURELOG::shutdown_compiler(this->scompiler);
- }
- }
-
- const std::vector<vpiHandle> &execute(std::unique_ptr<SURELOG::ErrorContainer> errors, std::unique_ptr<SURELOG::CommandLineParser> clp)
- {
- log_assert(!this->errors && !this->clp && !this->scompiler);
-
- bool success = true;
- bool noFatalErrors = true;
- unsigned int codedReturn = 0;
- clp->setWriteUhdm(false);
- errors->printMessages(clp->muteStdout());
- if (success && (!clp->help())) {
- this->scompiler = SURELOG::start_compiler(clp.get());
- if (!this->scompiler)
- codedReturn |= 1;
- this->designs.push_back(SURELOG::get_uhdm_design(this->scompiler));
- }
- SURELOG::ErrorContainer::Stats stats;
- if (!clp->help()) {
- stats = errors->getErrorStats();
- if (stats.nbFatal)
- codedReturn |= 1;
- if (stats.nbSyntax)
- codedReturn |= 2;
- }
- bool noFErrors = true;
- if (!clp->help())
- noFErrors = errors->printStats(stats, clp->muteStdout());
- if (noFErrors == false) {
- noFatalErrors = false;
- }
- if ((!noFatalErrors) || (!success) || (errors->getErrorStats().nbError))
- codedReturn |= 1;
- if (codedReturn) {
- log_error("Error when parsing design. Aborting!\n");
- }
-
- this->clp = std::move(clp);
- this->errors = std::move(errors);
-
- return this->designs;
- }
-
- private:
- std::unique_ptr<SURELOG::ErrorContainer> errors = nullptr;
- std::unique_ptr<SURELOG::CommandLineParser> clp = nullptr;
- SURELOG::scompiler *scompiler = nullptr;
- std::vector<vpiHandle> designs = {};
-};
-
-struct UhdmSurelogAstFrontend : public UhdmCommonFrontend {
- UhdmSurelogAstFrontend(std::string name, std::string short_help) : UhdmCommonFrontend(name, short_help) {}
- UhdmSurelogAstFrontend() : UhdmCommonFrontend("verilog_with_uhdm", "generate/read UHDM file") {}
-
- void help() override
- {
- // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
- log("\n");
- log(" read_verilog_with_uhdm [options] [filenames]\n");
- log("\n");
- log("Read SystemVerilog files using Surelog into the current design\n");
- log("\n");
- this->print_read_options();
- }
-
- AST::AstNode *parse(std::string filename) override
- {
- std::vector<const char *> cstrings;
- bool link = false;
- if (this->shared.formal) {
- systemverilog_defines.push_back("-DFORMAL=1");
- } else {
- systemverilog_defines.push_back("-DSYNTHESIS=1");
- }
- cstrings.reserve(this->args.size() + systemverilog_defaults.size() + systemverilog_defines.size());
- for (size_t i = 0; i < this->args.size(); ++i) {
- cstrings.push_back(const_cast<char *>(this->args[i].c_str()));
- if (this->args[i] == "-link")
- link = true;
- }
-
- if (!link) {
- // Add systemverilog defaults args
- for (size_t i = 0; i < systemverilog_defaults.size(); ++i) {
- // Convert args to surelog compatible
- if (systemverilog_defaults[i] == "-defer")
- this->shared.defer = true;
- // Pass any remainings args directly to surelog
- else
- cstrings.push_back(const_cast<char *>(systemverilog_defaults[i].c_str()));
- }
-
- // Add systemverilog defines args
- for (size_t i = 0; i < systemverilog_defines.size(); ++i)
- cstrings.push_back(const_cast<char *>(systemverilog_defines[i].c_str()));
- }
-
- auto symbolTable = std::make_unique<SURELOG::SymbolTable>();
- auto errors = std::make_unique<SURELOG::ErrorContainer>(symbolTable.get());
- auto clp = std::make_unique<SURELOG::CommandLineParser>(errors.get(), symbolTable.get(), false, false);
- bool success = clp->parseCommandLine(cstrings.size(), &cstrings[0]);
- if (!success) {
- log_error("Error parsing Surelog arguments!\n");
- }
- // Force -parse flag settings even if it wasn't specified
- clp->setwritePpOutput(true);
- clp->setParse(true);
- clp->fullSVMode(true);
- clp->setCacheAllowed(true);
- 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<UHDM::SynthSubset>(&serializer, this->shared.nonSynthesizableObjects, false);
- synthSubset->listenDesigns(uhdm_designs);
- delete synthSubset;
- }
-
- UhdmAst uhdm_ast(this->shared);
- AST::AstNode *current_ast = uhdm_ast.visit_designs(uhdm_designs);
-
- // FIXME: Check and reset remaining shared data
- this->shared.top_nodes.clear();
- this->shared.nonSynthesizableObjects.clear();
- return current_ast;
- }
- void call_log_header(RTLIL::Design *design) override { log_header(design, "Executing Verilog with UHDM frontend.\n"); }
-} UhdmSurelogAstFrontend;
-
-struct UhdmSystemVerilogFrontend : public UhdmSurelogAstFrontend {
- UhdmSystemVerilogFrontend() : UhdmSurelogAstFrontend("systemverilog", "read SystemVerilog files") {}
- void help() override
- {
- // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
- log("\n");
- log(" read_systemverilog [options] [filenames]\n");
- log("\n");
- log("Read SystemVerilog files using Surelog into the current design\n");
- log("\n");
- this->print_read_options();
- log(" -Ipath\n");
- log(" add include path.\n");
- log("\n");
- log(" -Pparameter=value\n");
- log(" define parameter as value.\n");
- log("\n");
- }
-} UhdmSystemVerilogFrontend;
-
-struct SystemVerilogDefaults : public Pass {
- SystemVerilogDefaults() : Pass("systemverilog_defaults", "set default options for read_systemverilog") {}
- void help() override
- {
- // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
- log("\n");
- log(" systemverilog_defaults -add [options]\n");
- log("\n");
- log("Add the specified options to the list of default options to read_systemverilog.\n");
- log("\n");
- log("\n");
- log(" systemverilog_defaults -clear\n");
- log("\n");
- log("Clear the list of Systemverilog default options.\n");
- log("\n");
- log("\n");
- log(" systemverilog_defaults -push\n");
- log(" systemverilog_defaults -pop\n");
- log("\n");
- log("Push or pop the list of default options to a stack. Note that -push does\n");
- log("not imply -clear.\n");
- log("\n");
- }
- void execute(std::vector<std::string> args, RTLIL::Design *) override
- {
- if (args.size() < 2)
- cmd_error(args, 1, "Missing argument.");
-
- if (args[1] == "-add") {
- systemverilog_defaults.insert(systemverilog_defaults.end(), args.begin() + 2, args.end());
- return;
- }
-
- if (args.size() != 2)
- cmd_error(args, 2, "Extra argument.");
-
- if (args[1] == "-clear") {
- systemverilog_defaults.clear();
- return;
- }
-
- if (args[1] == "-push") {
- systemverilog_defaults_stack.push_back(systemverilog_defaults);
- return;
- }
-
- if (args[1] == "-pop") {
- if (systemverilog_defaults_stack.empty()) {
- systemverilog_defaults.clear();
- } else {
- systemverilog_defaults.swap(systemverilog_defaults_stack.back());
- systemverilog_defaults_stack.pop_back();
- }
- return;
- }
- }
-} SystemVerilogDefaults;
-
-struct SystemVerilogDefines : public Pass {
- SystemVerilogDefines() : Pass("systemverilog_defines", "define and undefine systemverilog defines")
- {
- systemverilog_defines.push_back("-DYOSYS=1");
- }
- void help() override
- {
- // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
- log("\n");
- log(" systemverilog_defines [options]\n");
- log("\n");
- log("Define and undefine systemverilog preprocessor macros.\n");
- log("\n");
- log(" -Dname[=definition]\n");
- log(" define the preprocessor symbol 'name' and set its optional value\n");
- log(" 'definition'\n");
- log("\n");
- log(" -Uname[=definition]\n");
- log(" undefine the preprocessor symbol 'name'\n");
- log("\n");
- log(" -reset\n");
- log(" clear list of defined preprocessor symbols\n");
- log("\n");
- log(" -list\n");
- log(" list currently defined preprocessor symbols\n");
- log("\n");
- }
- void remove(const std::string name)
- {
- auto it = systemverilog_defines.begin();
- while (it != systemverilog_defines.end()) {
- std::string nm;
- size_t equal = (*it).find('=', 2);
- if (equal == std::string::npos)
- nm = (*it).substr(2, std::string::npos);
- else
- nm = (*it).substr(2, equal - 2);
- if (name == nm)
- systemverilog_defines.erase(it);
- else
- it++;
- }
- }
- void dump(void)
- {
- for (size_t i = 0; i < systemverilog_defines.size(); ++i) {
- std::string name, value = "";
- size_t equal = systemverilog_defines[i].find('=', 2);
- name = systemverilog_defines[i].substr(2, equal - 2);
- if (equal != std::string::npos)
- value = systemverilog_defines[i].substr(equal + 1, std::string::npos);
- Yosys::log("`define %s %s\n", name.c_str(), value.c_str());
- }
- }
- void execute(std::vector<std::string> args, RTLIL::Design *design) override
- {
- size_t argidx;
- for (argidx = 1; argidx < args.size(); argidx++) {
- std::string arg = args[argidx];
- if (arg == "-D" && argidx + 1 < args.size()) {
- systemverilog_defines.push_back("-D" + args[++argidx]);
- continue;
- }
- if (arg.compare(0, 2, "-D") == 0) {
- systemverilog_defines.push_back(arg);
- continue;
- }
- if (arg == "-U" && argidx + 1 < args.size()) {
- std::string name = args[++argidx];
- this->remove(name);
- continue;
- }
- if (arg.compare(0, 2, "-U") == 0) {
- std::string name = arg.substr(2);
- this->remove(name);
- continue;
- }
- if (arg == "-reset") {
- systemverilog_defines.erase(systemverilog_defines.begin() + 1, systemverilog_defines.end());
- continue;
- }
- if (arg == "-list") {
- this->dump();
- continue;
- }
- break;
- }
-
- if (args.size() != argidx)
- cmd_error(args, argidx, "Extra argument.");
- }
-} SystemVerilogDefines;
-
-} // namespace systemverilog_plugin
diff --git a/systemverilog-plugin/utils/memory.h b/systemverilog-plugin/utils/memory.h
deleted file mode 100644
index 6cd43c0..0000000
--- 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 <cassert>
-#include <utility>
-
-namespace systemverilog_plugin
-{
-
-// `std::default_delete` equivalent for any range of pointers, e.g. `std::vector<Object *>`.
-template <class Range> 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 <class T> 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 <class R, class AlwaysVoid_ = void> struct default_resource_deleter {
- using type = missing_type;
-};
-
-// Type trait for detecting whether type `R` is any range of pointers.
-template <class R, class ValueType_ = std::remove_reference_t<decltype(*std::begin(std::declval<R>()))>>
-using is_range_of_pointers_t = std::enable_if_t<std::is_pointer_v<ValueType_> && !std::is_array_v<ValueType_>>;
-
-// Overload for any range of pointers.
-template <class R> struct default_resource_deleter<R, is_range_of_pointers_t<R>> {
- using type = default_delete_ptr_range<R>;
-};
-
-// Convenience alias.
-template <class R> using default_resource_deleter_t = typename default_resource_deleter<R>::type;
-
-// Type trait for checking whether type `R` is a valid deleter of type `D`.
-template <class R, class D, class = void> struct is_valid_resource_deleter : std::false_type {
-};
-template <class R, class D> struct is_valid_resource_deleter<R, D, std::void_t<decltype(std::declval<D>()(std::declval<R &>()))>> : std::true_type {
-};
-
-// Convenience alias.
-template <class R, class D> inline constexpr bool is_valid_resource_deleter_v = is_valid_resource_deleter<R, D>::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 Resource, class Deleter = utils_memory_internal::default_resource_deleter_t<Resource>> 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<Deleter, utils_memory_internal::missing_type>;
- 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<Resource, Deleter>;
- 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<Resource, std::remove_cv_t<std::remove_reference_t<Resource>>>;
- 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 <class OtherResource> unique_resource(OtherResource &&other) : resource(std::forward<OtherResource>(other)), initialized(true) {}
-
- // Copy
-
- unique_resource(const unique_resource &) = delete;
-
- unique_resource &operator=(const unique_resource &) = delete;
-
- // Move
-
- template <class OtherDeleter>
- unique_resource(unique_resource<Resource, OtherDeleter> &&other) : resource(std::move(other.resource)), initialized(other.initialized)
- {
- other.initialized = false;
- }
-
- template <class OtherDeleter> unique_resource &operator=(unique_resource<Resource, OtherDeleter> &&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 <class OtherResource> void reset(OtherResource &&other)
- {
- if (initialized) {
- Deleter{}(resource);
- }
- resource = std::forward<OtherResource>(other);
- initialized = true;
- }
-};
-
-// Creates `unique_resource<Resource, Deleter>` and initializes it with a resource constructed using specified arguments.
-template <class Resource, class Deleter = utils_memory_internal::default_resource_deleter_t<Resource>, class... Tn>
-inline unique_resource<Resource, Deleter> make_unique_resource(Tn &&... arg_n)
-{
- return unique_resource<Resource, Deleter>(Resource(std::forward<Tn>(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 4f67830..0000000
--- 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 cecd427..0000000
--- 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 1c6f1d8..0000000
--- 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 9fde149..0000000
--- 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<std::string> plugin_aliases;
- load_plugin("systemverilog", plugin_aliases);
- }
-} UhdmDummy;
-
-PRIVATE_NAMESPACE_END