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."
881833aa73
This commit is contained in:
Jeff Wang 2018-03-09 12:47:11 +00:00 committed by Jeff Wang
parent 2bda51ac34
commit 16ea4ea61a
5 changed files with 207 additions and 17 deletions

View File

@ -88,6 +88,8 @@ std::string AST::type2str(AstNodeType type)
X(AST_LIVE) X(AST_LIVE)
X(AST_FAIR) X(AST_FAIR)
X(AST_COVER) X(AST_COVER)
X(AST_ENUM)
X(AST_ENUM_ITEM)
X(AST_FCALL) X(AST_FCALL)
X(AST_TO_BITS) X(AST_TO_BITS)
X(AST_TO_SIGNED) X(AST_TO_SIGNED)
@ -202,6 +204,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
is_logic = false; is_logic = false;
is_signed = false; is_signed = false;
is_string = false; is_string = false;
is_enum = false;
is_wand = false; is_wand = false;
is_wor = false; is_wor = false;
is_unsized = false; is_unsized = false;
@ -321,6 +324,9 @@ void AstNode::dumpAst(FILE *f, std::string indent) const
fprintf(f, " %d", v); fprintf(f, " %d", v);
fprintf(f, " ]"); fprintf(f, " ]");
} }
if (is_enum) {
fprintf(f, " type=enum");
}
fprintf(f, "\n"); fprintf(f, "\n");
for (auto &it : attributes) { 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 n : design->verilog_packages){
for (auto o : n->children) { for (auto o : n->children) {
AstNode *cloned_node = o->clone(); 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); (*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)); 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()); design->verilog_packages.push_back((*it)->clone());
else }
else {
// must be global definition
design->verilog_globals.push_back((*it)->clone()); design->verilog_globals.push_back((*it)->clone());
}
} }
} }

View File

@ -68,6 +68,8 @@ namespace AST
AST_LIVE, AST_LIVE,
AST_FAIR, AST_FAIR,
AST_COVER, AST_COVER,
AST_ENUM,
AST_ENUM_ITEM,
AST_FCALL, AST_FCALL,
AST_TO_BITS, AST_TO_BITS,
@ -181,6 +183,8 @@ namespace AST
int port_id, range_left, range_right; int port_id, range_left, range_right;
uint32_t integer; uint32_t integer;
double realvalue; 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 // if this is a multirange memory then this vector contains offset and length of each dimension
std::vector<int> multirange_dimensions; std::vector<int> multirange_dimensions;
@ -285,6 +289,9 @@ namespace AST
int isConst() const; // return '1' for AST_CONSTANT and '2' for AST_REALVALUE int isConst() const; // return '1' for AST_CONSTANT and '2' for AST_REALVALUE
double asReal(bool is_signed); double asReal(bool is_signed);
RTLIL::Const realAsConst(int width); 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 // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code

View File

@ -595,6 +595,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
switch (type) switch (type)
{ {
case AST_NONE:
// unallocated enum, ignore
break;
case AST_CONSTANT: case AST_CONSTANT:
width_hint = max(width_hint, int(bits.size())); width_hint = max(width_hint, int(bits.size()));
if (!is_signed) if (!is_signed)
@ -612,7 +615,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
id_ast = current_scope.at(str); id_ast = current_scope.at(str);
if (!id_ast) if (!id_ast)
log_file_error(filename, linenum, "Failed to resolve identifier %s for width detection!\n", str.c_str()); 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) { 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; this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1;
} else } else
@ -861,6 +864,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
case AST_GENIF: case AST_GENIF:
case AST_GENCASE: case AST_GENCASE:
case AST_PACKAGE: case AST_PACKAGE:
case AST_ENUM:
case AST_MODPORT: case AST_MODPORT:
case AST_MODPORTMEMBER: case AST_MODPORTMEMBER:
case AST_TYPEDEF: case AST_TYPEDEF:
@ -1022,7 +1026,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
else else
log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); 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) if (id2ast->children[0]->type != AST_CONSTANT)
log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n", str.c_str()); log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n", str.c_str());
chunk = RTLIL::Const(id2ast->children[0]->bits); chunk = RTLIL::Const(id2ast->children[0]->bits);

View 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.) // 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; 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; const_fold = true;
// in certain cases a function must be evaluated constant. this is what in_param controls. // 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; in_param = true;
std::map<std::string, AstNode*> backup_scope; 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; 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 || 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_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL ||
node->type == AST_TYPEDEF) { node->type == AST_TYPEDEF) {
backup_scope[node->str] = current_scope[node->str]; backup_scope[node->str] = current_scope[node->str];
current_scope[node->str] = node; 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++) { for (size_t i = 0; i < children.size(); i++) {
AstNode *node = children[i]; AstNode *node = children[i];
@ -492,8 +503,21 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
} }
break; 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_PARAMETER:
case AST_LOCALPARAM: case AST_LOCALPARAM:
case AST_ENUM_ITEM:
while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true) while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true)
did_something = true; did_something = true;
children[0]->detectSignWidth(width_hint, sign_hint); 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 // Insert clones children from template at beginning
for (int i = 0; i < GetSize(templ->children); i++) for (int i = 0; i < GetSize(templ->children); i++)
children.insert(children.begin() + i, templ->children[i]->clone()); children.insert(children.begin() + i, templ->children[i]->clone());
if (type == AST_MEMORY && GetSize(children) == 1) { if (type == AST_MEMORY && GetSize(children) == 1) {
// Single-bit memories must have [0:0] range // Single-bit memories must have [0:0] range
AstNode *rng = new AstNode(AST_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; did_something = true;
} }
log_assert(!is_custom_type); log_assert(!is_custom_type);
} }
// resolve constant prefixes // resolve constant prefixes
if (type == AST_PREFIX) { 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 // 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.size() > 1 && children[1]->type == AST_RANGE) {
if (!children[1]->range_valid) if (!children[1]->range_valid)
log_file_error(filename, linenum, "Non-constant width range on parameter decl.\n"); 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 (type == AST_IDENTIFIER) {
if (current_scope.count(str) == 0) { if (current_scope.count(str) == 0) {
for (auto node : current_ast_mod->children) { 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 || //log("looking at mod scope child %s\n", type2str(node->type).c_str());
node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION) && str == node->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; current_scope[node->str] = node;
break; 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) 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; AstNode *wire = nullptr;
@ -2588,7 +2634,7 @@ replace_fcall_later:;
switch (type) switch (type)
{ {
case AST_IDENTIFIER: 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 (current_scope[str]->children[0]->type == AST_CONSTANT) {
if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) { if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) {
std::vector<RTLIL::State> data; 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++) { for (size_t i = 0; i < children.size(); i++) {
AstNode *child = children[i]; AstNode *child = children[i];
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_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) if (backup_name_map.size() == 0)
backup_name_map = name_map; backup_name_map = name_map;
std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; 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); 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 YOSYS_NAMESPACE_END

View File

@ -108,6 +108,20 @@ struct specify_rise_fall {
specify_triple 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} %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> 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 <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 <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 <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff
%type <al> attr case_attr %type <al> attr case_attr
@ -428,7 +443,9 @@ package:
}; };
package_body: package_body:
package_body package_body_stmt |; package_body package_body_stmt
| // optional
;
package_body_stmt: package_body_stmt:
typedef_decl | typedef_decl |
@ -604,6 +621,7 @@ module_body:
module_body_stmt: module_body_stmt:
task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_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; always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
checker_decl: checker_decl:
@ -1224,6 +1242,77 @@ single_defparam_decl:
ast_stack.back()->children.push_back(node); 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: wire_decl:
attr wire_type range { attr wire_type range {
albuf = $1; albuf = $1;