From 16ea4ea61abd8f000564b3bfe1c88246c1caf631 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Fri, 9 Mar 2018 12:47:11 +0000 Subject: partial rebase of PeterCrozier's enum work onto current master 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 | 23 +++++++-- frontends/ast/ast.h | 7 +++ frontends/ast/genrtlil.cc | 8 +++- frontends/ast/simplify.cc | 95 +++++++++++++++++++++++++++++++++----- frontends/verilog/verilog_parser.y | 91 +++++++++++++++++++++++++++++++++++- 5 files changed, 207 insertions(+), 17 deletions(-) diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 5bbea0faf..135750837 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -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()); + } } } diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 918d178c7..ffdcd9271 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -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 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 diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 94f5c0a04..76705c75f 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -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); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index b94a8d710..9013ebe66 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -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 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 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 diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index a30935e0a..66dcf1fec 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -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 range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int %type wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list %type opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id +%type opt_enum_init %type opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff %type 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; -- cgit v1.2.3 From 5ddf84d430a176acf5ab638a86c84484277bee84 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Thu, 16 Jan 2020 17:17:42 -0500 Subject: allow enum typedefs --- frontends/verilog/verilog_parser.y | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 66dcf1fec..08db36276 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -1523,7 +1523,12 @@ typedef_decl: ast_stack.back()->children.push_back(new AstNode(AST_TYPEDEF, astbuf1)); ast_stack.back()->children.back()->str = *$4; - }; + } | + TOK_TYPEDEF enum_type TOK_ID ';' { + ast_stack.back()->children.push_back(new AstNode(AST_TYPEDEF, astbuf1)); + ast_stack.back()->children.back()->str = *$3; + } + ; cell_stmt: attr TOK_ID { -- cgit v1.2.3 From cc2236d0c02be096ad1b92209072be3f16598933 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Thu, 16 Jan 2020 17:11:08 -0500 Subject: lexer doesn't seem to return TOK_REG for logic anymore --- frontends/verilog/verilog_parser.y | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 08db36276..ea0a09599 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -1261,13 +1261,14 @@ enum_type: TOK_ENUM { enum_base_type: int_vec param_range | int_atom - | /* nothing */ { addRange(astbuf1); } + | /* nothing */ {astbuf1->is_reg = true; addRange(astbuf1); } ; -int_atom: TOK_INTEGER { addRange(astbuf1); } // probably should do byte, range [7:0] here +int_atom: TOK_INTEGER {astbuf1->is_reg=true; 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 +int_vec: TOK_REG {astbuf1->is_reg = true;} + | TOK_LOGIC {astbuf1->is_logic = true;} ; enum_name_list: -- cgit v1.2.3 From febe7706a2013a834f1bcd1200a9ac9d997e79c4 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Thu, 16 Jan 2020 12:03:27 -0500 Subject: simple enum test --- tests/svtypes/enum_simple.sv | 47 ++++++++++++++++++++++++++++++++++++++++++++ tests/svtypes/enum_simple.ys | 5 +++++ 2 files changed, 52 insertions(+) create mode 100644 tests/svtypes/enum_simple.sv create mode 100644 tests/svtypes/enum_simple.ys diff --git a/tests/svtypes/enum_simple.sv b/tests/svtypes/enum_simple.sv new file mode 100644 index 000000000..0c3f55c34 --- /dev/null +++ b/tests/svtypes/enum_simple.sv @@ -0,0 +1,47 @@ + +module enum_simple(input clk, input rst); + + enum {s0, s1, s2, s3} test_enum; + typedef enum logic [1:0] { + ts0, ts1, ts2, ts3 + } states_t; + (states_t) state; + (states_t) enum_const = s1; + + always @(posedge clk) begin + if (rst) begin + test_enum <= s3; + state <= ts0; + end else begin + //test_enum + if (test_enum == s0) + test_enum <= s1; + else if (test_enum == s1) + test_enum <= s2; + else if (test_enum == s2) + test_enum <= s3; + else if (test_enum == s3) + test_enum <= s0; + else + assert(1'b0); //should be unreachable + + //state + if (state == ts0) + state <= ts1; + else if (state == ts1) + state <= ts2; + else if (state == ts2) + state <= ts0; + else + assert(1'b0); //should be unreachable + end + end + + always @(*) begin + assert(state != 2'h3); + assert(s0 == '0); + assert(ts0 == '0); + assert(enum_const == s1); + end + +endmodule diff --git a/tests/svtypes/enum_simple.ys b/tests/svtypes/enum_simple.ys new file mode 100644 index 000000000..79981657b --- /dev/null +++ b/tests/svtypes/enum_simple.ys @@ -0,0 +1,5 @@ + +read_verilog -sv enum_simple.sv +hierarchy; proc; opt +sat -verify -seq 1 -set-at 1 rst 1 -tempinduct -prove-asserts -show-all + -- cgit v1.2.3 From caf35896da98225518f06ebcd57b010c958de652 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Thu, 16 Jan 2020 12:03:42 -0500 Subject: enum in package test --- tests/svtypes/typedef_package.sv | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/svtypes/typedef_package.sv b/tests/svtypes/typedef_package.sv index a1e16d4b1..b766f10cf 100644 --- a/tests/svtypes/typedef_package.sv +++ b/tests/svtypes/typedef_package.sv @@ -1,11 +1,14 @@ package pkg; typedef logic [7:0] uint8_t; + typedef enum logic [7:0] {bb=8'hBB} enum8_t; endpackage module top; (* keep *) (pkg::uint8_t) a = 8'hAA; + (* keep *) (pkg::enum8_t) b_enum = pkg::bb; always @* assert(a == 8'hAA); + always @* assert(b_enum == 8'hBB); endmodule -- cgit v1.2.3 From 41a0a93dcc489fbba2624006ef52a3d0f565ee25 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Thu, 16 Jan 2020 15:23:03 -0500 Subject: allow enums to be declared at toplevel scope --- frontends/ast/simplify.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 9013ebe66..2e1b1e404 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -428,6 +428,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY || node->type == AST_TYPEDEF) while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM)) did_something = true; + if (node->type == AST_ENUM) { + for (auto enode : node->children){ + log_assert(enode->type==AST_ENUM_ITEM); + while (node->simplify(true, false, false, 1, -1, false, node->type == AST_ENUM_ITEM)) + did_something = true; + } + } } } -- cgit v1.2.3 From 549deb637305a4bf4c9e2c58388b149ee8fa6089 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Thu, 16 Jan 2020 17:05:45 -0500 Subject: fix enum in generate blocks --- frontends/ast/simplify.cc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 2e1b1e404..c96f5d549 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -3097,6 +3097,26 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma child->str = new_name; current_scope[new_name] = child; } + if (child->type == AST_ENUM){ + for (auto enode : child->children){ + log_assert(enode->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; + size_t pos = enode->str.rfind('.'); + if (pos == std::string::npos) + pos = enode->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0; + else + pos = pos + 1; + new_name = enode->str.substr(0, pos) + new_name + enode->str.substr(pos); + if (new_name[0] != '$' && new_name[0] != '\\') + new_name = prefix[0] + new_name; + name_map[enode->str] = new_name; + + enode->str = new_name; + current_scope[new_name] = enode; + } + } } for (size_t i = 0; i < children.size(); i++) { -- cgit v1.2.3 From 8ef5c7d48cc395535fe589d9798ad9985729b635 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Thu, 16 Jan 2020 17:12:00 -0500 Subject: scoped enum tests --- tests/svtypes/typedef_scopes.sv | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/svtypes/typedef_scopes.sv b/tests/svtypes/typedef_scopes.sv index faa385bd6..9b3331c60 100644 --- a/tests/svtypes/typedef_scopes.sv +++ b/tests/svtypes/typedef_scopes.sv @@ -1,23 +1,35 @@ typedef logic [3:0] outer_uint4_t; +typedef enum logic {s0, s1} outer_enum_t; module top; (outer_uint4_t) u4_i = 8'hA5; + (outer_enum_t) enum4_i = s0; always @(*) assert(u4_i == 4'h5); + always @(*) assert(enum4_i == 1'b0); typedef logic [3:0] inner_type; + typedef enum logic [2:0] {s2=2, s3, s4} inner_enum_t; (inner_type) inner_i1 = 8'h5A; + (inner_enum_t) inner_enum1 = s3; always @(*) assert(inner_i1 == 4'hA); + always @(*) assert(inner_enum1 == 3'h3); if (1) begin: genblock typedef logic [7:0] inner_type; - (inner_type) inner_gb_i = 8'hA5; + parameter (inner_type) inner_const = 8'hA5; + typedef enum logic [2:0] {s5=5, s6, s7} inner_enum_t; + (inner_type) inner_gb_i = inner_const; //8'hA5; + (inner_enum_t) inner_gb_enum1 = s7; always @(*) assert(inner_gb_i == 8'hA5); + always @(*) assert(inner_gb_enum1 == 3'h7); end (inner_type) inner_i2 = 8'h42; + (inner_type) inner_enum2 = s4; always @(*) assert(inner_i2 == 4'h2); + always @(*) assert(inner_enum2 == 3'h4); endmodule -- cgit v1.2.3 From 98c6bd76306db8d0cb6428d1e60d132c965132ac Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Fri, 17 Jan 2020 01:21:09 -0500 Subject: fix bug introduced by not taking all of PeterCrozier's changes in 16ea4ea6 The if(str == node->str) is in fact necessary (otherwise causes generate for in Multiplier_2D in tests/simple/multiplier.v to fail with error message "Right hand side of 3rd expression of generate for-loop is not constant!"). Note: in PeterCrozier's implementation, the break only breaks out of the switch-case, not the outer for loop. --- frontends/ast/simplify.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index c96f5d549..0c9e32d24 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -324,7 +324,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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_LOCALPARAM || type == AST_ENUM_ITEM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX) + if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_ENUM_ITEM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX) in_param = true; std::map backup_scope; @@ -1089,8 +1089,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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; + if (str == node->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) { @@ -1327,7 +1329,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (buf->type != AST_CONSTANT) - log_file_error(filename, linenum, "Right hand side of 3rd expression of generate for-loop is not constant!\n"); + log_file_error(filename, linenum, "Right hand side of 3rd expression of generate for-loop is not constant (%s)!\n", type2str(buf->type).c_str()); delete varbuf->children[0]; varbuf->children[0] = buf; -- cgit v1.2.3 From 6320f2692bc97d9d447622c1ba55a90cfe9dd411 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Mon, 3 Feb 2020 01:08:16 -0500 Subject: separate out enum_item/param implementation when they should be different --- frontends/ast/simplify.cc | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 0c9e32d24..95c50cda0 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -324,7 +324,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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_ENUM_ITEM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX) + if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX) in_param = true; std::map backup_scope; @@ -418,8 +418,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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; } } } @@ -431,7 +429,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (node->type == AST_ENUM) { for (auto enode : node->children){ log_assert(enode->type==AST_ENUM_ITEM); - while (node->simplify(true, false, false, 1, -1, false, node->type == AST_ENUM_ITEM)) + while (node->simplify(true, false, false, 1, -1, false, in_param)) did_something = true; } } @@ -511,10 +509,10 @@ 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); + //log("\nENUM %s: %d child %d\n", str.c_str(), 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) + while (!item_node->basic_prep && item_node->simplify(false, false, false, stage, -1, false, in_param)) did_something = true; } // allocate values (called more than once) @@ -524,7 +522,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); @@ -536,6 +533,18 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1); } break; + case AST_ENUM_ITEM: + while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, in_param)) + did_something = true; + children[0]->detectSignWidth(width_hint, sign_hint); + if (children.size() > 1 && children[1]->type == AST_RANGE) { + while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, in_param)) + did_something = true; + if (!children[1]->range_valid) + log_file_error(filename, linenum, "Non-constant width range on enum item decl.\n"); + width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1); + } + break; case AST_TO_BITS: case AST_TO_SIGNED: -- cgit v1.2.3 From d12ba42a741464d410773471813d0a78a7ae1db2 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Mon, 3 Feb 2020 01:12:24 -0500 Subject: add attributes for enumerated values in ilang - information also useful for strongly-typed enums (not implemented) - resolves enum values in ilang part of #1594 - still need to output enums to VCD (or better yet FST) files --- frontends/ast/ast.cc | 1 + frontends/ast/simplify.cc | 68 +++++++++++++++++++++++++++++++++++++- frontends/verilog/verilog_parser.y | 9 ++++- tests/svtypes/enum_simple.sv | 4 +-- tests/svtypes/typedef_scopes.sv | 2 +- 5 files changed, 79 insertions(+), 5 deletions(-) diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 135750837..239813810 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -1222,6 +1222,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump } else { // must be global definition + (*it)->simplify(false, false, false, 1, -1, false, false); //process enum/other declarations design->verilog_globals.push_back((*it)->clone()); } } diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 95c50cda0..b9fb37d50 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -413,6 +413,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, current_scope[node->str] = node; } if (node->type == AST_ENUM) { + current_scope[node->str] = node; for (auto enode : node->children) { log_assert(enode->type==AST_ENUM_ITEM); if (current_scope.count(enode->str) == 0) { @@ -862,6 +863,63 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, range_swapped = templ->range_swapped; range_left = templ->range_left; range_right = templ->range_right; + attributes["\\wiretype"] = mkconst_str(resolved_type->str); + //check if enum + if (templ->attributes.count("\\enum_type")){ + //get reference to enum node: + std::string enum_type = templ->attributes["\\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); + AstNode *enum_node = current_scope.at(enum_type); + log_assert(enum_node->type == AST_ENUM); + //get width from 1st enum item: + log_assert(enum_node->children.size() >= 1); + AstNode *enum_item0 = enum_node->children[0]; + log_assert(enum_item0->type == 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 == 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 == 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_"; + enum_item_str.append(std::to_string(width)); + enum_item_str.append("_"); + //get enum item value + if(enum_item->children[0]->type != 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() + ); + } + int val = enum_item->children[0]->asInt(is_signed); + enum_item_str.append(std::to_string(val)); + //set attribute for available val to enum item name mappings + attributes[enum_item_str.c_str()] = mkconst_str(enum_item->str); + } + } // Insert clones children from template at beginning for (int i = 0; i < GetSize(templ->children); i++) @@ -908,6 +966,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, range_swapped = templ->range_swapped; range_left = templ->range_left; range_right = templ->range_right; + attributes["\\wiretype"] = mkconst_str(resolved_type->str); for (auto template_child : templ->children) children.push_back(template_child->clone()); did_something = true; @@ -1104,10 +1163,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } break; case AST_ENUM: + current_scope[node->str] = node; 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()); + //log("\nadding enum item %s to scope\n", str.c_str()); current_scope[str] = enum_node; } } @@ -2577,6 +2637,10 @@ skip_dynamic_range_lvalue_expansion:; wire->is_output = false; wire->is_reg = true; wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false); + if (child->type == AST_ENUM_ITEM){ + wire->attributes["\\enum_base_type"] = child->attributes["\\enum_base_type"]; + + } wire_cache[child->str] = wire; current_ast_mod->children.push_back(wire); @@ -3109,6 +3173,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma current_scope[new_name] = child; } if (child->type == AST_ENUM){ + current_scope[child->str] = child; for (auto enode : child->children){ log_assert(enode->type == AST_ENUM_ITEM); if (backup_name_map.size() == 0) @@ -3872,6 +3937,7 @@ void AstNode::allocateDefaultEnumValues() int last_enum_int = -1; for (auto node : children) { log_assert(node->type==AST_ENUM_ITEM); + node->attributes["\\enum_base_type"] = mkconst_str(str); for (size_t i = 0; i < node->children.size(); i++) { switch (node->children[i]->type) { case AST_NONE: diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index ea0a09599..f25a8de28 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -1243,9 +1243,12 @@ single_defparam_decl: }; enum_type: TOK_ENUM { + static int enum_count; // create parent node for the enum astbuf2 = new AstNode(AST_ENUM); ast_stack.back()->children.push_back(astbuf2); + astbuf2->str = std::string("$enum"); + astbuf2->str += std::to_string(enum_count++); // create the template for the names astbuf1 = new AstNode(AST_ENUM_ITEM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); @@ -1254,6 +1257,7 @@ enum_type: TOK_ENUM { delete astbuf1; astbuf1 = tnode; tnode->type = AST_WIRE; + tnode->attributes["\\enum_type"] = AstNode::mkconst_str(astbuf2->str); // drop constant but keep any range delete tnode->children[0]; tnode->children.erase(tnode->children.begin()); } @@ -1311,7 +1315,10 @@ enum_var: TOK_ID { } ; -enum_decl: enum_type enum_var_list ';' { delete astbuf1; } +enum_decl: enum_type enum_var_list ';' { + //enum_type creates astbuf1 for use by typedef only + delete astbuf1; + } ; wire_decl: diff --git a/tests/svtypes/enum_simple.sv b/tests/svtypes/enum_simple.sv index 0c3f55c34..ccaf50da0 100644 --- a/tests/svtypes/enum_simple.sv +++ b/tests/svtypes/enum_simple.sv @@ -6,7 +6,7 @@ module enum_simple(input clk, input rst); ts0, ts1, ts2, ts3 } states_t; (states_t) state; - (states_t) enum_const = s1; + (states_t) enum_const = ts1; always @(posedge clk) begin if (rst) begin @@ -41,7 +41,7 @@ module enum_simple(input clk, input rst); assert(state != 2'h3); assert(s0 == '0); assert(ts0 == '0); - assert(enum_const == s1); + assert(enum_const == ts1); end endmodule diff --git a/tests/svtypes/typedef_scopes.sv b/tests/svtypes/typedef_scopes.sv index 9b3331c60..1c45c7057 100644 --- a/tests/svtypes/typedef_scopes.sv +++ b/tests/svtypes/typedef_scopes.sv @@ -27,7 +27,7 @@ module top; end (inner_type) inner_i2 = 8'h42; - (inner_type) inner_enum2 = s4; + (inner_enum_t) inner_enum2 = s4; always @(*) assert(inner_i2 == 4'h2); always @(*) assert(inner_enum2 == 3'h4); -- cgit v1.2.3 From a31ba8e5d5b5eefdea35aaec585118dca6287673 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Mon, 17 Feb 2020 04:40:02 -0500 Subject: remove unnecessary blank line --- frontends/ast/simplify.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index b9fb37d50..69056aeee 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -2637,10 +2637,9 @@ skip_dynamic_range_lvalue_expansion:; wire->is_output = false; wire->is_reg = true; wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false); - if (child->type == AST_ENUM_ITEM){ + if (child->type == AST_ENUM_ITEM) wire->attributes["\\enum_base_type"] = child->attributes["\\enum_base_type"]; - } wire_cache[child->str] = wire; current_ast_mod->children.push_back(wire); -- cgit v1.2.3 From 1c16311d104caa19e8aff35a0df1595c73fa1638 Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Mon, 17 Feb 2020 04:40:18 -0500 Subject: update documentation for enums and typedefs --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 77e9410da..696a4d0b1 100644 --- a/README.md +++ b/README.md @@ -437,6 +437,17 @@ Verilog Attributes and non-standard features ... endmodule +- The ``wiretype`` attribute is added by the verilog parser for wires of a + typedef'd type to indicate the type identifier. + +- Various ``enum_{width}_{value}`` attributes are added to wires of an + enumerated type to give a map of possible enum items to their values. + +- The ``enum_base_type`` attribute is added to enum items to indicate which + enum they belong to (enums -- anonymous and otherwise -- are + automatically named with an auto-incrementing counter). Note that enums + are currently not strongly typed. + - A limited subset of DPI-C functions is supported. The plugin mechanism (see ``help plugin``) can be used to load .so files with implementations of DPI-C routines. As a non-standard extension it is possible to specify @@ -527,6 +538,12 @@ from SystemVerilog: SystemVerilog files being read into the same design afterwards. - typedefs are supported (including inside packages) + - type identifiers must currently be enclosed in (parentheses) when declaring + signals of that type (this is syntactically incorrect SystemVerilog) + - type casts are currently not supported + +- enums are supported (including inside packages) + - but are currently not strongly typed - SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether ports are inputs or outputs are supported. -- cgit v1.2.3