From ee2b5b7ed186414897a8a570a9e503c438803ad8 Mon Sep 17 00:00:00 2001 From: Rupert Swarbrick Date: Mon, 20 Apr 2020 16:06:53 +0100 Subject: [PATCH] Generate an RTLIL representation of bind constructs 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 | 3 ++ frontends/ast/Makefile.inc | 1 + frontends/ast/ast.cc | 5 +++ frontends/ast/ast.h | 3 ++ frontends/ast/ast_binding.cc | 49 ++++++++++++++++++++++ frontends/ast/ast_binding.h | 58 ++++++++++++++++++++++++++ frontends/ast/genrtlil.cc | 79 +++++++++++++++++++++++++++++++++++- kernel/binding.cc | 29 +++++++++++++ kernel/binding.h | 60 +++++++++++++++++++++++++++ kernel/rtlil.cc | 17 ++++++++ kernel/rtlil.h | 11 ++++- 11 files changed, 312 insertions(+), 3 deletions(-) create mode 100644 frontends/ast/ast_binding.cc create mode 100644 frontends/ast/ast_binding.h create mode 100644 kernel/binding.cc create mode 100644 kernel/binding.h diff --git a/Makefile b/Makefile index 6b64b0e35..a4d8007e4 100644 --- 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)"' diff --git a/frontends/ast/Makefile.inc b/frontends/ast/Makefile.inc index 91d917c91..9e6eee1e8 100644 --- a/frontends/ast/Makefile.inc +++ b/frontends/ast/Makefile.inc @@ -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 diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index fe503c547..4fbc238b0 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -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) diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index ba5a11b96..63104bca4 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -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 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 index 000000000..c20d1df4d --- /dev/null +++ b/frontends/ast/ast_binding.cc @@ -0,0 +1,49 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#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 index 000000000..641497d52 --- /dev/null +++ b/frontends/ast/ast_binding.h @@ -0,0 +1,58 @@ +/* -*- c++ -*- + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * This 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 + +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 ast_node; + }; +} + +YOSYS_NAMESPACE_END + +#endif diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 45aab9d8e..c82664b98 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -28,8 +28,10 @@ #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 #include @@ -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 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 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(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 index 000000000..621f7007b --- /dev/null +++ b/kernel/binding.cc @@ -0,0 +1,29 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#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 index 000000000..3b64e76da --- /dev/null +++ b/kernel/binding.h @@ -0,0 +1,60 @@ +/* -*- c++ -*- + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#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 diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index b414556f3..40b9b761a 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -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 &wires) { log_assert(refcount_wires_ == 0); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index dc0d5234b..50707c0ae 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -72,6 +72,7 @@ namespace RTLIL struct MemWriteAction; struct SyncRule; struct Process; + struct Binding; typedef std::pair SigSig; @@ -1033,6 +1034,8 @@ struct RTLIL::Design int refcount_modules_; dict modules_; + std::vector bindings_; + std::vector verilog_packages, verilog_globals; std::unique_ptr 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 wires_; dict cells_; - std::vector connections_; + + std::vector connections_; + std::vector bindings_; RTLIL::IdString name; idict avail_parameters; @@ -1207,6 +1214,8 @@ public: RTLIL::ObjRange wires() { return RTLIL::ObjRange(&wires_, &refcount_wires_); } RTLIL::ObjRange cells() { return RTLIL::ObjRange(&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 &wires); void remove(RTLIL::Cell *cell); -- 2.30.2