partial rebase of PeterCrozier's enum work onto current master
authorJeff Wang <jjj11x@gmail.com>
Fri, 9 Mar 2018 12:47:11 +0000 (12:47 +0000)
committerJeff Wang <jeffrey.wang@ll.mit.edu>
Thu, 16 Jan 2020 18:51:47 +0000 (13:51 -0500)
I tried to keep only the enum-related changes, and minimize the diff. (The
original commit also had a lot of work done to get typedefs working, but yosys
has diverged quite a bit since the 2018-03-09 commit, with a new typedef
implementation.) I did not include the import related changes either.

Original commit:
"Initial implementation of enum, typedef, import.  Still a WIP."
https://github.com/PeterCrozier/yosys/commit/881833aa738e7404987646ea8076284e911fce3f

frontends/ast/ast.cc
frontends/ast/ast.h
frontends/ast/genrtlil.cc
frontends/ast/simplify.cc
frontends/verilog/verilog_parser.y

index 5bbea0faf690877d86c2363d6db306c928579166..135750837bb95ece188ecc3eb779e97d59bc11f1 100644 (file)
@@ -88,6 +88,8 @@ std::string AST::type2str(AstNodeType type)
        X(AST_LIVE)
        X(AST_FAIR)
        X(AST_COVER)
+       X(AST_ENUM)
+       X(AST_ENUM_ITEM)
        X(AST_FCALL)
        X(AST_TO_BITS)
        X(AST_TO_SIGNED)
@@ -202,6 +204,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
        is_logic = false;
        is_signed = false;
        is_string = false;
+       is_enum = false;
        is_wand = false;
        is_wor = false;
        is_unsized = false;
@@ -321,6 +324,9 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
                        fprintf(f, " %d", v);
                fprintf(f, " ]");
        }
+       if (is_enum) {
+               fprintf(f, " type=enum");
+       }
        fprintf(f, "\n");
 
        for (auto &it : attributes) {
@@ -1174,7 +1180,15 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
                        for (auto n : design->verilog_packages){
                                for (auto o : n->children) {
                                        AstNode *cloned_node = o->clone();
-                                       cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1);
+                                       log("cloned node %s\n", type2str(cloned_node->type).c_str());
+                                       if (cloned_node->type == AST_ENUM){
+                                               for (auto e : cloned_node->children){
+                                                       log_assert(e->type == AST_ENUM_ITEM);
+                                                       e->str = n->str + std::string("::") + e->str.substr(1);
+                                               }
+                                       } else {
+                                               cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1);
+                                       }
                                        (*it)->children.push_back(cloned_node);
                                }
                        }
@@ -1203,10 +1217,13 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
 
                        design->add(process_module(*it, defer));
                }
-               else if ((*it)->type == AST_PACKAGE)
+               else if ((*it)->type == AST_PACKAGE) {
                        design->verilog_packages.push_back((*it)->clone());
-               else
+               }
+               else {
+                       // must be global definition
                        design->verilog_globals.push_back((*it)->clone());
+               }
        }
 }
 
index 918d178c7bfcc91ff1c813361e696a6b6323c54e..ffdcd927132ed7a2cbb027264edd13310507eecb 100644 (file)
@@ -68,6 +68,8 @@ namespace AST
                AST_LIVE,
                AST_FAIR,
                AST_COVER,
+               AST_ENUM,
+               AST_ENUM_ITEM,
 
                AST_FCALL,
                AST_TO_BITS,
@@ -181,6 +183,8 @@ namespace AST
                int port_id, range_left, range_right;
                uint32_t integer;
                double realvalue;
+               // set for IDs typed to an enumeration, not used
+               bool is_enum;
 
                // if this is a multirange memory then this vector contains offset and length of each dimension
                std::vector<int> multirange_dimensions;
@@ -285,6 +289,9 @@ namespace AST
                int isConst() const; // return '1' for AST_CONSTANT and '2' for AST_REALVALUE
                double asReal(bool is_signed);
                RTLIL::Const realAsConst(int width);
+
+               // helpers for enum
+               void allocateDefaultEnumValues();
        };
 
        // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
index 94f5c0a044d14b42f4f051d1842509a8e88e7367..76705c75f8e3b94f474d920f691afdf25f378150 100644 (file)
@@ -595,6 +595,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
 
        switch (type)
        {
+       case AST_NONE:
+               // unallocated enum, ignore
+               break;
        case AST_CONSTANT:
                width_hint = max(width_hint, int(bits.size()));
                if (!is_signed)
@@ -612,7 +615,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
                        id_ast = current_scope.at(str);
                if (!id_ast)
                        log_file_error(filename, linenum, "Failed to resolve identifier %s for width detection!\n", str.c_str());
-               if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM) {
+               if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM || id_ast->type == AST_ENUM_ITEM) {
                        if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) {
                                this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1;
                        } else
@@ -861,6 +864,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
        case AST_GENIF:
        case AST_GENCASE:
        case AST_PACKAGE:
+       case AST_ENUM:
        case AST_MODPORT:
        case AST_MODPORTMEMBER:
        case AST_TYPEDEF:
@@ -1022,7 +1026,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
                                else
                                        log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
                        }
-                       else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) {
+                       else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM || id2ast->type == AST_ENUM_ITEM) {
                                if (id2ast->children[0]->type != AST_CONSTANT)
                                        log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n", str.c_str());
                                chunk = RTLIL::Const(id2ast->children[0]->bits);
index b94a8d710fab73bb00bba33d765a911bd335e951..9013ebe66a31c1b47f902be058c98c9441afa930 100644 (file)
@@ -318,13 +318,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
        }
 
        // activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
-       if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF)
+       if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_ENUM_ITEM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF)
                const_fold = true;
-       if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM))
+       if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM || current_scope[str]->type == AST_ENUM_ITEM))
                const_fold = true;
 
        // in certain cases a function must be evaluated constant. this is what in_param controls.
-       if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX)
+       if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_LOCALPARAM || type == AST_ENUM_ITEM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX)
                in_param = true;
 
        std::map<std::string, AstNode*> backup_scope;
@@ -405,12 +405,23 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
                                }
                                this_wire_scope[node->str] = node;
                        }
+                       // these nodes appear at the top level in a module and can define names
                        if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
                                        node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL ||
                                        node->type == AST_TYPEDEF) {
                                backup_scope[node->str] = current_scope[node->str];
                                current_scope[node->str] = node;
                        }
+                       if (node->type == AST_ENUM) {
+                               for (auto enode : node->children) {
+                                       log_assert(enode->type==AST_ENUM_ITEM);
+                                       if (current_scope.count(enode->str) == 0) {
+                                               current_scope[enode->str] = enode;
+                                       }
+                                       //      while (enode->simplify(true, false, false, 1, -1, false, true))
+                                       //              did_something = true;
+                               }
+                       }
                }
                for (size_t i = 0; i < children.size(); i++) {
                        AstNode *node = children[i];
@@ -492,8 +503,21 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
                }
                break;
 
+       case AST_ENUM:
+               // log("\nENUM %d child %d\n", basic_prep, children[0]->basic_prep);
+               if (!basic_prep) {
+                       for (auto item_node : children) {
+                               while (!item_node->basic_prep && item_node->simplify(false, false, false, stage, -1, false, true) == true)
+                                       did_something = true;
+                       }
+                       // allocate values (called more than once)
+                       allocateDefaultEnumValues();
+               }
+               break;
+
        case AST_PARAMETER:
        case AST_LOCALPARAM:
+       case AST_ENUM_ITEM:
                while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true)
                        did_something = true;
                children[0]->detectSignWidth(width_hint, sign_hint);
@@ -826,7 +850,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
                        // Insert clones children from template at beginning
                        for (int i  = 0; i < GetSize(templ->children); i++)
                                children.insert(children.begin() + i, templ->children[i]->clone());
-                       
+
                        if (type == AST_MEMORY && GetSize(children) == 1) {
                                // Single-bit memories must have [0:0] range
                                AstNode *rng = new AstNode(AST_RANGE);
@@ -873,7 +897,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
                        did_something = true;
                }
                log_assert(!is_custom_type);
-       }       
+       }
 
        // resolve constant prefixes
        if (type == AST_PREFIX) {
@@ -1005,7 +1029,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
        }
 
        // trim/extend parameters
-       if (type == AST_PARAMETER || type == AST_LOCALPARAM) {
+       if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_ENUM_ITEM) {
                if (children.size() > 1 && children[1]->type == AST_RANGE) {
                        if (!children[1]->range_valid)
                                log_file_error(filename, linenum, "Non-constant width range on parameter decl.\n");
@@ -1046,10 +1070,32 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
        if (type == AST_IDENTIFIER) {
                if (current_scope.count(str) == 0) {
                        for (auto node : current_ast_mod->children) {
-                               if ((node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR ||
-                                               node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION) && str == node->str) {
+                               //log("looking at mod scope child %s\n", type2str(node->type).c_str());
+                               switch (node->type) {
+                               case AST_PARAMETER:
+                               case AST_LOCALPARAM:
+                               case AST_WIRE:
+                               case AST_AUTOWIRE:
+                               case AST_GENVAR:
+                               case AST_MEMORY:
+                               case AST_FUNCTION:
+                               case AST_TASK:
+                               case AST_DPI_FUNCTION:
+                                       //log("found child %s, %s\n", type2str(node->type).c_str(), node->str.c_str());
+                                       log("add %s, type %s to scope\n", str.c_str(), type2str(node->type).c_str());
                                        current_scope[node->str] = node;
                                        break;
+                               case AST_ENUM:
+                                       for (auto enum_node : node->children) {
+                                               log_assert(enum_node->type==AST_ENUM_ITEM);
+                                               if (str == enum_node->str) {
+                                                       log("\nadding enum %s to scope\n", str.c_str());
+                                                       current_scope[str] = enum_node;
+                                               }
+                                       }
+                                       break;
+                               default:
+                                       break;
                                }
                        }
                }
@@ -2482,7 +2528,7 @@ skip_dynamic_range_lvalue_expansion:;
                }
 
                for (auto child : decl->children)
-                       if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM)
+                       if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || child->type == AST_ENUM_ITEM)
                        {
                                AstNode *wire = nullptr;
 
@@ -2588,7 +2634,7 @@ replace_fcall_later:;
                switch (type)
                {
                case AST_IDENTIFIER:
-                       if (current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) {
+                       if (current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM || current_scope[str]->type == AST_ENUM_ITEM)) {
                                if (current_scope[str]->children[0]->type == AST_CONSTANT) {
                                        if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) {
                                                std::vector<RTLIL::State> data;
@@ -3025,7 +3071,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
        for (size_t i = 0; i < children.size(); i++) {
                AstNode *child = children[i];
                if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM ||
-                               child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF) {
+                               child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF || child->type == AST_ENUM_ITEM) {
                        if (backup_name_map.size() == 0)
                                backup_name_map = name_map;
                        std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix;
@@ -3782,4 +3828,31 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
        return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed);
 }
 
+void AstNode::allocateDefaultEnumValues()
+{
+       log_assert(type==AST_ENUM);
+       int last_enum_int = -1;
+       for (auto node : children) {
+               log_assert(node->type==AST_ENUM_ITEM);
+               for (size_t i = 0; i < node->children.size(); i++) {
+                       switch (node->children[i]->type) {
+                       case AST_NONE:
+                               // replace with auto-incremented constant
+                               delete node->children[i];
+                               node->children[i] = AstNode::mkconst_int(++last_enum_int, true);
+                               break;
+                       case AST_CONSTANT:
+                               // explicit constant (or folded expression)
+                               // TODO: can't extend 'x or 'z item
+                               last_enum_int = node->children[i]->integer;
+                               break;
+                       default:
+                               // ignore ranges
+                               break;
+                       }
+                       // TODO: range check
+               }
+       }
+}
+
 YOSYS_NAMESPACE_END
index a30935e0a3d2a0312edb9478bf61c39eb689b79a..66dcf1fec94df7c62f85482d42508cb8de1d22fd 100644 (file)
@@ -108,6 +108,20 @@ struct specify_rise_fall {
        specify_triple fall;
 };
 
+static AstNode *makeRange(int msb = 31, int lsb = 0, bool isSigned = true)
+{
+       auto range = new AstNode(AST_RANGE);
+       range->children.push_back(AstNode::mkconst_int(msb, true));
+       range->children.push_back(AstNode::mkconst_int(lsb, true));
+       range->is_signed = isSigned;
+       return range;
+}
+
+static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = true)
+{
+       auto range = makeRange(msb, lsb, isSigned);
+       parent->children.push_back(range);
+}
 %}
 
 %define api.prefix {frontend_verilog_yy}
@@ -157,6 +171,7 @@ struct specify_rise_fall {
 %type <ast> range range_or_multirange  non_opt_range non_opt_multirange range_or_signed_int
 %type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list
 %type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id
+%type <ast> opt_enum_init
 %type <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff
 %type <al> attr case_attr
 
@@ -428,7 +443,9 @@ package:
        };
 
 package_body:
-       package_body package_body_stmt |;
+       package_body package_body_stmt
+       | // optional
+       ;
 
 package_body_stmt:
        typedef_decl |
@@ -604,6 +621,7 @@ module_body:
 
 module_body_stmt:
        task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
+       enum_decl |
        always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
 
 checker_decl:
@@ -1224,6 +1242,77 @@ single_defparam_decl:
                ast_stack.back()->children.push_back(node);
        };
 
+enum_type: TOK_ENUM {
+               // create parent node for the enum
+               astbuf2 = new AstNode(AST_ENUM);
+               ast_stack.back()->children.push_back(astbuf2);
+               // create the template for the names
+               astbuf1 = new AstNode(AST_ENUM_ITEM);
+               astbuf1->children.push_back(AstNode::mkconst_int(0, true));
+        } param_signed enum_base_type '{' enum_name_list '}' {  // create template for the enum vars
+                                                               auto tnode = astbuf1->clone();
+                                                               delete astbuf1;
+                                                               astbuf1 = tnode;
+                                                               tnode->type = AST_WIRE;
+                                                               // drop constant but keep any range
+                                                               delete tnode->children[0];
+                                                               tnode->children.erase(tnode->children.begin()); }
+        ;
+
+enum_base_type: int_vec param_range
+       | int_atom
+       | /* nothing */         { addRange(astbuf1); }
+       ;
+
+int_atom: TOK_INTEGER          { addRange(astbuf1); }          // probably should do byte, range [7:0] here
+       ;
+
+int_vec: TOK_REG               { astbuf1->is_reg = true; }     // lexer returns this for logic|bit too
+       ;
+
+enum_name_list:
+       enum_name_decl
+       | enum_name_list ',' enum_name_decl
+       ;
+
+enum_name_decl:
+       TOK_ID opt_enum_init {
+               // put in fn
+               log_assert(astbuf1);
+               log_assert(astbuf2);
+               auto node = astbuf1->clone();
+               node->str = *$1;
+               delete $1;
+               delete node->children[0];
+               node->children[0] = $2 ?: new AstNode(AST_NONE);
+               astbuf2->children.push_back(node);
+       }
+       ;
+
+opt_enum_init:
+       '=' basic_expr          { $$ = $2; }    // TODO: restrict this
+       | /* optional */        { $$ = NULL; }
+       ;
+
+enum_var_list:
+       enum_var
+       | enum_var_list ',' enum_var
+       ;
+
+enum_var: TOK_ID {
+               log_assert(astbuf1);
+               log_assert(astbuf2);
+               auto node = astbuf1->clone();
+               ast_stack.back()->children.push_back(node);
+               node->str = *$1;
+               delete $1;
+               node->is_enum = true;
+       }
+       ;
+
+enum_decl: enum_type enum_var_list ';'                 { delete astbuf1; }
+       ;
+
 wire_decl:
        attr wire_type range {
                albuf = $1;