Generate an RTLIL representation of bind constructs
authorRupert Swarbrick <rswarbrick@gmail.com>
Mon, 20 Apr 2020 15:06:53 +0000 (16:06 +0100)
committerZachary Snow <zachary.j.snow@gmail.com>
Fri, 13 Aug 2021 23:11:35 +0000 (17:11 -0600)
This code now takes the AST nodes of type AST_BIND and generates a
representation in the RTLIL for them.

This is a little tricky, because a binding of the form:

    bind baz foo_t foo_i (.arg (1 + bar));

means "make an instance of foo_t called foo_i, instantiate it inside
baz and connect the port arg to the result of the expression 1+bar".
Of course, 1+bar needs a cell for the addition. Where should that cell
live?

With this patch, the Binding structure that represents the construct
is itself an AST::AstModule module. This lets us put the adder cell
inside it. We'll pull the contents out and plonk them into 'baz' when
we actually do the binding operation as part of the hierarchy pass.

Of course, we don't want RTLIL::Binding to contain an
AST::AstModule (since kernel code shouldn't depend on a frontend), so
we define RTLIL::Binding as an abstract base class and put the
AST-specific code into an AST::Binding subclass. This is analogous to
the AST::AstModule class.

Makefile
frontends/ast/Makefile.inc
frontends/ast/ast.cc
frontends/ast/ast.h
frontends/ast/ast_binding.cc [new file with mode: 0644]
frontends/ast/ast_binding.h [new file with mode: 0644]
frontends/ast/genrtlil.cc
kernel/binding.cc [new file with mode: 0644]
kernel/binding.h [new file with mode: 0644]
kernel/rtlil.cc
kernel/rtlil.h

index 6b64b0e35a85bee180f5a250b6e656bda335c514..a4d8007e465ea76b984fc4530d8ad21df5df42da 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -565,6 +565,7 @@ $(eval $(call add_include_file,kernel/yosys.h))
 $(eval $(call add_include_file,kernel/hashlib.h))
 $(eval $(call add_include_file,kernel/log.h))
 $(eval $(call add_include_file,kernel/rtlil.h))
+$(eval $(call add_include_file,kernel/binding.h))
 $(eval $(call add_include_file,kernel/register.h))
 $(eval $(call add_include_file,kernel/celltypes.h))
 $(eval $(call add_include_file,kernel/celledges.h))
@@ -585,6 +586,7 @@ $(eval $(call add_include_file,libs/sha1/sha1.h))
 $(eval $(call add_include_file,libs/json11/json11.hpp))
 $(eval $(call add_include_file,passes/fsm/fsmdata.h))
 $(eval $(call add_include_file,frontends/ast/ast.h))
+$(eval $(call add_include_file,frontends/ast/ast_binding.h))
 $(eval $(call add_include_file,frontends/blif/blifparse.h))
 $(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
 $(eval $(call add_include_file,backends/cxxrtl/cxxrtl.h))
@@ -595,6 +597,7 @@ $(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.cc))
 $(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.h))
 
 OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o
+OBJS += kernel/binding.o
 ifeq ($(ENABLE_ABC),1)
 ifneq ($(ABCEXTERNAL),)
 kernel/yosys.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
index 91d917c9169947245fbd335ecfed46317d353da4..9e6eee1e8fbc814bdc9abd66969037f406cae11e 100644 (file)
@@ -3,4 +3,5 @@ OBJS += frontends/ast/ast.o
 OBJS += frontends/ast/simplify.o
 OBJS += frontends/ast/genrtlil.o
 OBJS += frontends/ast/dpicall.o
+OBJS += frontends/ast/ast_binding.o
 
index fe503c547a9b61c219f950bea9f5b0db4f20ceb1..4fbc238b02db8b28ee2e28351c9018673943d985 100644 (file)
@@ -1310,6 +1310,11 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
                        design->verilog_packages.push_back(child->clone());
                        current_scope.clear();
                }
+               else if (child->type == AST_BIND) {
+                       // top-level bind construct
+                       for (RTLIL::Binding *binding : child->genBindings())
+                               design->add(binding);
+               }
                else {
                        // must be global definition
                        if (child->type == AST_PARAMETER)
index ba5a11b9683ff24be7bc01ec8fd17533fd97239a..63104bca4b9246811f3142e29db405c07193a5b0 100644 (file)
@@ -284,6 +284,9 @@ namespace AST
                void dumpAst(FILE *f, std::string indent) const;
                void dumpVlog(FILE *f, std::string indent) const;
 
+               // Generate RTLIL for a bind construct
+               std::vector<RTLIL::Binding *> genBindings() const;
+
                // used by genRTLIL() for detecting expression width and sign
                void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL);
                void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = NULL);
diff --git a/frontends/ast/ast_binding.cc b/frontends/ast/ast_binding.cc
new file mode 100644 (file)
index 0000000..c20d1df
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  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.
+ *
+ */
+
+#include "ast_binding.h"
+#include "ast.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+using namespace AST_INTERNAL;
+
+AST::Binding::Binding(RTLIL::IdString  target_type,
+                      RTLIL::IdString  target_name,
+                      const AstNode   &cell)
+       : RTLIL::Binding(target_type, target_name),
+         ast_node(cell.clone())
+{
+       log_assert(cell.type == AST_CELL);
+}
+
+std::string
+AST::Binding::describe() const
+{
+       std::ostringstream oss;
+       oss << "directive to bind " << ast_node->str
+           << " to " << target_name.str();
+       if (!target_type.empty())
+               oss << " (target type: "
+                   << target_type.str()
+                   << ")";
+       return oss.str();
+}
+
+PRIVATE_NAMESPACE_END
diff --git a/frontends/ast/ast_binding.h b/frontends/ast/ast_binding.h
new file mode 100644 (file)
index 0000000..641497d
--- /dev/null
@@ -0,0 +1,58 @@
+/* -*- c++ -*-
+ *  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 header declares the AST::Binding class
+ *
+ *  This is used to support the bind directive and is to RTLIL::Binding as
+ *  AST::AstModule is to RTLIL::Module, holding a syntax-level representation of
+ *  cells until we get to a stage where they make sense. In the case of a bind
+ *  directive, this is when we elaborate the design in the hierarchy pass.
+ *
+ */
+
+#ifndef AST_BINDING_H
+#define AST_BINDING_H
+
+#include "kernel/rtlil.h"
+#include "kernel/binding.h"
+
+#include <memory>
+
+YOSYS_NAMESPACE_BEGIN
+
+namespace AST
+{
+       class Binding : public RTLIL::Binding
+       {
+       public:
+               Binding(RTLIL::IdString  target_type,
+                       RTLIL::IdString  target_name,
+                       const AstNode   &cell);
+
+               std::string describe() const override;
+
+       private:
+               // The syntax-level representation of the cell to be bound.
+               std::unique_ptr<AstNode> ast_node;
+       };
+}
+
+YOSYS_NAMESPACE_END
+
+#endif
index 45aab9d8ebee32e7682a1ffb5a5e927890b561ac..c82664b98a47b345b164de31a91d0dbfaef39e8b 100644 (file)
 
 #include "kernel/log.h"
 #include "kernel/utils.h"
+#include "kernel/binding.h"
 #include "libs/sha1/sha1.h"
 #include "ast.h"
+#include "ast_binding.h"
 
 #include <sstream>
 #include <stdarg.h>
@@ -733,6 +735,69 @@ struct AST_INTERNAL::ProcessGenerator
        }
 };
 
+// Generate RTLIL for a bind construct
+//
+// The AST node will have one or more AST_IDENTIFIER children, which were added
+// by bind_target_instance in the parser. After these, it will have one or more
+// cells, as parsed by single_cell. These have type AST_CELL.
+//
+// If there is more than one AST_IDENTIFIER, the first one should be considered
+// a module identifier. If there is only one AST_IDENTIFIER, we can't tell at
+// this point whether it's a module/interface name or the name of an instance
+// because the correct interpretation depends on what's visible at elaboration
+// time. For now, we just treat it as a target instance with unknown type, and
+// we'll deal with the corner case in the hierarchy pass.
+//
+// To simplify downstream code, RTLIL::Binding only has a single target and
+// single bound instance. If we see the syntax that allows more than one of
+// either, we split it into multiple Binding objects.
+std::vector<RTLIL::Binding *> AstNode::genBindings() const
+{
+       // Partition children into identifiers and cells
+       int num_ids = 0;
+       for (int i = 0; i < GetSize(children); ++i) {
+               if (children[i]->type != AST_IDENTIFIER) {
+                       log_assert(i > 0);
+                       num_ids = i;
+                       break;
+               }
+       }
+
+       // We should have found at least one child that's not an identifier
+       log_assert(num_ids > 0);
+
+       // Make sense of the identifiers, extracting a possible type name and a
+       // list of hierarchical IDs. We represent an unknown type with an empty
+       // string.
+       RTLIL::IdString tgt_type;
+       int first_tgt_inst = 0;
+       if (num_ids > 1) {
+               tgt_type = children[0]->str;
+               first_tgt_inst = 1;
+       }
+
+       std::vector<RTLIL::Binding *> ret;
+
+       // At this point, we know that children with index >= first_tgt_inst and
+       // index < num_ids are (hierarchical?) names of target instances. Make a
+       // binding object for each of them, and fill in the generated instance
+       // cells each time.
+       for (int i = first_tgt_inst; i < num_ids; ++i) {
+               const AstNode &tgt_child = *children[i];
+
+               for (int j = num_ids; j < GetSize(children); ++j) {
+                       const AstNode &cell_child = *children[j];
+
+                       log_assert(cell_child.type == AST_CELL);
+
+                       ret.push_back(new AST::Binding(tgt_type, tgt_child.str,
+                                                      cell_child));
+               }
+       }
+
+       return ret;
+}
+
 // detect sign and width of an expression
 void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real)
 {
@@ -1311,7 +1376,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
                                RTLIL::Wire *wire = current_module->addWire(str);
                                set_src_attr(wire, this);
                                wire->name = str;
-                               if (flag_autowire)
+
+                               // If we are currently processing a bind directive which wires up
+                               // signals or parameters explicitly, rather than with .*, then
+                               // current_module will start out empty and we don't want to warn the
+                               // user about it: we'll spot broken wiring later, when we run the
+                               // hierarchy pass.
+                               if (dynamic_cast<RTLIL::Binding*>(current_module)) {
+                                       /* nothing to do here */
+                               } else if (flag_autowire)
                                        log_file_warning(filename, location.first_line, "Identifier `%s' is implicitly declared.\n", str.c_str());
                                else
                                        log_file_error(filename, location.first_line, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
@@ -1975,7 +2048,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
                } break;
 
        case AST_BIND: {
-               // The bind construct. Currently unimplemented: just ignore it.
+               // Read a bind construct. This should have one or more cells as children.
+               for (RTLIL::Binding *binding : genBindings())
+                       current_module->add(binding);
                break;
        }
 
diff --git a/kernel/binding.cc b/kernel/binding.cc
new file mode 100644 (file)
index 0000000..621f700
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *  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.
+ *
+ */
+
+#include "binding.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+RTLIL::Binding::Binding(RTLIL::IdString target_type,
+                        RTLIL::IdString target_name)
+       : target_type(target_type), target_name(target_name)
+{}
+
+YOSYS_NAMESPACE_END
diff --git a/kernel/binding.h b/kernel/binding.h
new file mode 100644 (file)
index 0000000..3b64e76
--- /dev/null
@@ -0,0 +1,60 @@
+/* -*- c++ -*-
+ *  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.
+ *
+ */
+
+#ifndef BINDING_H
+#define BINDING_H
+
+#include "kernel/rtlil.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct RTLIL::Binding
+{
+       // Represents a bind construct.
+       //
+       // The target of the binding is represented by target_type and
+       // target_name (see comments above the fields).
+
+       Binding(RTLIL::IdString target_type,
+               RTLIL::IdString target_name);
+
+       virtual ~Binding() {}
+
+       // Return a string describing the binding
+       virtual std::string describe() const = 0;
+
+protected:
+       // May be empty. If not, it's the name of the module or interface to
+       // bind to.
+       RTLIL::IdString target_type;
+
+       // If target_type is nonempty (the usual case), this is a hierarchical
+       // reference to the bind target. If target_type is empty, we have to
+       // wait until the hierarchy pass to figure out whether this was the name
+       // of a module/interface type or an instance.
+       RTLIL::IdString target_name;
+
+       // An attribute name which contains an ID that's unique across binding
+       // instances (used to ensure we don't apply a binding twice to a module)
+       RTLIL::IdString attr_name;
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
index b414556f36c045644c5207f268ec17f7f52225f4..40b9b761aa20b8835b4f4a3c0e17c97faa93ee4e 100644 (file)
@@ -20,6 +20,7 @@
 #include "kernel/yosys.h"
 #include "kernel/macc.h"
 #include "kernel/celltypes.h"
+#include "kernel/binding.h"
 #include "frontends/verilog/verilog_frontend.h"
 #include "frontends/verilog/preproc.h"
 #include "backends/rtlil/rtlil_backend.h"
@@ -573,6 +574,8 @@ RTLIL::Design::~Design()
 {
        for (auto &pr : modules_)
                delete pr.second;
+       for (auto n : bindings_)
+               delete n;
        for (auto n : verilog_packages)
                delete n;
        for (auto n : verilog_globals)
@@ -636,6 +639,12 @@ void RTLIL::Design::add(RTLIL::Module *module)
        }
 }
 
+void RTLIL::Design::add(RTLIL::Binding *binding)
+{
+       log_assert(binding != nullptr);
+       bindings_.push_back(binding);
+}
+
 RTLIL::Module *RTLIL::Design::addModule(RTLIL::IdString name)
 {
        if (modules_.count(name) != 0)
@@ -872,6 +881,8 @@ RTLIL::Module::~Module()
                delete pr.second;
        for (auto &pr : processes)
                delete pr.second;
+       for (auto binding : bindings_)
+               delete binding;
 #ifdef WITH_PYTHON
        RTLIL::Module::get_all_modules()->erase(hashidx_);
 #endif
@@ -1923,6 +1934,12 @@ void RTLIL::Module::add(RTLIL::Process *process)
        process->module = this;
 }
 
+void RTLIL::Module::add(RTLIL::Binding *binding)
+{
+       log_assert(binding != nullptr);
+       bindings_.push_back(binding);
+}
+
 void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
 {
        log_assert(refcount_wires_ == 0);
index dc0d5234b98662f07f006431d35f3e4df351144b..50707c0aee4b7625ba6cd8f5c4580ad67bc1aac0 100644 (file)
@@ -72,6 +72,7 @@ namespace RTLIL
        struct MemWriteAction;
        struct SyncRule;
        struct Process;
+       struct Binding;
 
        typedef std::pair<SigSpec, SigSpec> SigSig;
 
@@ -1033,6 +1034,8 @@ struct RTLIL::Design
 
        int refcount_modules_;
        dict<RTLIL::IdString, RTLIL::Module*> modules_;
+       std::vector<RTLIL::Binding*> bindings_;
+
        std::vector<AST::AstNode*> verilog_packages, verilog_globals;
        std::unique_ptr<define_map_t> verilog_defines;
 
@@ -1053,6 +1056,8 @@ struct RTLIL::Design
        }
 
        void add(RTLIL::Module *module);
+       void add(RTLIL::Binding *binding);
+
        RTLIL::Module *addModule(RTLIL::IdString name);
        void remove(RTLIL::Module *module);
        void rename(RTLIL::Module *module, RTLIL::IdString new_name);
@@ -1140,7 +1145,9 @@ public:
 
        dict<RTLIL::IdString, RTLIL::Wire*> wires_;
        dict<RTLIL::IdString, RTLIL::Cell*> cells_;
-       std::vector<RTLIL::SigSig> connections_;
+
+       std::vector<RTLIL::SigSig>   connections_;
+       std::vector<RTLIL::Binding*> bindings_;
 
        RTLIL::IdString name;
        idict<RTLIL::IdString> avail_parameters;
@@ -1207,6 +1214,8 @@ public:
        RTLIL::ObjRange<RTLIL::Wire*> wires() { return RTLIL::ObjRange<RTLIL::Wire*>(&wires_, &refcount_wires_); }
        RTLIL::ObjRange<RTLIL::Cell*> cells() { return RTLIL::ObjRange<RTLIL::Cell*>(&cells_, &refcount_cells_); }
 
+       void add(RTLIL::Binding *binding);
+
        // Removing wires is expensive. If you have to remove wires, remove them all at once.
        void remove(const pool<RTLIL::Wire*> &wires);
        void remove(RTLIL::Cell *cell);