aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClaire Wolf <clifford@clifford.at>2020-02-20 18:17:25 +0100
committerGitHub <noreply@github.com>2020-02-20 18:17:25 +0100
commitcd044a2bb6adf7a5e00d4a6c075e9489d852d733 (patch)
tree025e868ba44465bba10799be98e89e1aea77894a
parentcd60f079d6c96e1d8a00c3c081fab1148d432b44 (diff)
parent1c16311d104caa19e8aff35a0df1595c73fa1638 (diff)
downloadyosys-cd044a2bb6adf7a5e00d4a6c075e9489d852d733.tar.gz
yosys-cd044a2bb6adf7a5e00d4a6c075e9489d852d733.tar.bz2
yosys-cd044a2bb6adf7a5e00d4a6c075e9489d852d733.zip
Merge pull request #1642 from jjj11x/jjj11x/sv-enum
Enum support
-rw-r--r--README.md17
-rw-r--r--frontends/ast/ast.cc24
-rw-r--r--frontends/ast/ast.h7
-rw-r--r--frontends/ast/genrtlil.cc8
-rw-r--r--frontends/ast/simplify.cc198
-rw-r--r--frontends/verilog/verilog_parser.y106
-rw-r--r--tests/svtypes/enum_simple.sv47
-rw-r--r--tests/svtypes/enum_simple.ys5
-rw-r--r--tests/svtypes/typedef_package.sv3
-rw-r--r--tests/svtypes/typedef_scopes.sv14
10 files changed, 410 insertions, 19 deletions
diff --git a/README.md b/README.md
index 28c525d78..9c15fe3d9 100644
--- a/README.md
+++ b/README.md
@@ -446,6 +446,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
@@ -536,6 +547,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.
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 5bbea0faf..239813810 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,14 @@ 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
+ (*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/ast.h b/frontends/ast/ast.h
index 14e1cec5e..a50ae306d 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<int> multirange_dimensions;
@@ -286,6 +290,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 fe0412699..57107b76a 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -323,9 +323,9 @@ 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.
@@ -410,18 +410,35 @@ 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) {
+ current_scope[node->str] = node;
+ for (auto enode : node->children) {
+ log_assert(enode->type==AST_ENUM_ITEM);
+ if (current_scope.count(enode->str) == 0) {
+ current_scope[enode->str] = enode;
+ }
+ }
+ }
}
for (size_t i = 0; i < children.size(); i++) {
AstNode *node = children[i];
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, in_param))
+ did_something = true;
+ }
+ }
}
}
@@ -497,6 +514,18 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
break;
+ case AST_ENUM:
+ //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, in_param))
+ did_something = true;
+ }
+ // allocate values (called more than once)
+ allocateDefaultEnumValues();
+ }
+ break;
+
case AST_PARAMETER:
case AST_LOCALPARAM:
while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true)
@@ -510,6 +539,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:
@@ -827,11 +868,68 @@ 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++)
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,12 +971,13 @@ 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;
}
log_assert(!is_custom_type);
- }
+ }
// resolve constant prefixes
if (type == AST_PREFIX) {
@@ -1010,7 +1109,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");
@@ -1051,9 +1150,34 @@ 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());
+ 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:
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 item %s to scope\n", str.c_str());
+ current_scope[str] = enum_node;
+ }
+ }
+ break;
+ default:
break;
}
}
@@ -1279,7 +1403,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;
@@ -2498,7 +2622,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;
@@ -2529,6 +2653,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)
+ wire->attributes["\\enum_base_type"] = child->attributes["\\enum_base_type"];
+
wire_cache[child->str] = wire;
current_ast_mod->children.push_back(wire);
@@ -2604,7 +2731,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;
@@ -3051,7 +3178,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;
@@ -3070,6 +3197,27 @@ 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){
+ current_scope[child->str] = child;
+ 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++) {
@@ -3808,4 +3956,32 @@ 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);
+ 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:
+ // 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 8840cf4e8..9001aa162 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 <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,85 @@ single_defparam_decl:
ast_stack.back()->children.push_back(node);
};
+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));
+ } 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;
+ 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()); }
+ ;
+
+enum_base_type: int_vec param_range
+ | int_atom
+ | /* nothing */ {astbuf1->is_reg = true; addRange(astbuf1); }
+ ;
+
+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;}
+ | TOK_LOGIC {astbuf1->is_logic = true;}
+ ;
+
+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 ';' {
+ //enum_type creates astbuf1 for use by typedef only
+ delete astbuf1;
+ }
+ ;
+
wire_decl:
attr wire_type range {
albuf = $1;
@@ -1434,7 +1531,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 {
diff --git a/tests/svtypes/enum_simple.sv b/tests/svtypes/enum_simple.sv
new file mode 100644
index 000000000..ccaf50da0
--- /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 = ts1;
+
+ 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 == ts1);
+ 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
+
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
diff --git a/tests/svtypes/typedef_scopes.sv b/tests/svtypes/typedef_scopes.sv
index faa385bd6..1c45c7057 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_enum_t) inner_enum2 = s4;
always @(*) assert(inner_i2 == 4'h2);
+ always @(*) assert(inner_enum2 == 3'h4);
endmodule