diff options
author | Peter Crozier <peter@crozier.com> | 2020-05-08 14:40:49 +0100 |
---|---|---|
committer | Peter Crozier <peter@crozier.com> | 2020-05-08 14:40:49 +0100 |
commit | 0b6b47ca670b9219bcb81ab7d3599267c2ef7571 (patch) | |
tree | 7f185ed9add86d52c5f55828e07b190420c77511 /frontends/ast/simplify.cc | |
parent | aafaeb66dfd839b8223059884d2741dadc9e2d92 (diff) | |
download | yosys-0b6b47ca670b9219bcb81ab7d3599267c2ef7571.tar.gz yosys-0b6b47ca670b9219bcb81ab7d3599267c2ef7571.tar.bz2 yosys-0b6b47ca670b9219bcb81ab7d3599267c2ef7571.zip |
Implement SV structs.
Diffstat (limited to 'frontends/ast/simplify.cc')
-rw-r--r-- | frontends/ast/simplify.cc | 334 |
1 files changed, 233 insertions, 101 deletions
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 318ffc1be..ce435c26b 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -168,6 +168,110 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg } +void AstNode::annotateTypedEnums(AstNode *template_node) +{ + //check if enum + if (template_node->attributes.count(ID::enum_type)) { + //get reference to enum node: + std::string enum_type = template_node->attributes[ID::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_value_"; + //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() + ); + } + RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed); + enum_item_str.append(val.as_string()); + //set attribute for available val to enum item name mappings + attributes[enum_item_str.c_str()] = mkconst_str(enum_item->str); + } + } +} + +static bool name_has_dot(const std::string &name, std::string &struct_name) +{ + // check if plausible struct member name \sss.mmm + std::string::size_type pos; + if (name.substr(0, 1) == "\\" && (pos = name.find('.', 0)) != std::string::npos) { + struct_name = name.substr(0, pos); + return true; + } + return false; +} + +static AstNode *make_range(int left, int right, bool is_signed = false) +{ + // generate a pre-validated range node for a fixed signal range. + auto range = new AstNode(AST_RANGE); + range->range_left = left; + range->range_right = right; + range->range_valid = true; + range->children.push_back(AstNode::mkconst_int(left, true)); + range->children.push_back(AstNode::mkconst_int(right, true)); + range->is_signed = is_signed; + return range; +} + +static AstNode *make_packed_struct(AstNode *template_node, std::string &name) +{ + // create a wire for the packed struct + auto wnode = new AstNode(AST_WIRE); + wnode->str = name; + wnode->is_logic = true; + wnode->range_valid = true; + // get the width from the MS member in the template + // as members are laid out from left to right + int offset = template_node->children[0]->range_left; + auto range = make_range(offset, 0); + wnode->children.push_back(range); + // make sure this node is the one in scope for this name + current_scope[name] = wnode; + // add members to scope + for (auto *node : template_node->children) { + auto member_name = name + "." + node->str; + current_scope[member_name] = node; + } + return wnode; +} + // convert the AST into a simpler AST that has all parameters substituted by their // values, unrolled for-loops, expanded generate blocks, etc. when this function // is done with an AST it can be converted into RTLIL using genRTLIL(). @@ -567,6 +671,53 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } break; + case AST_STRUCT: + //log("STRUCT %d %d %d\n", stage, basic_prep, in_param); + if (!basic_prep) { + //dumpAst(NULL, "1> "); + for (auto *node : children) { + // resolve any ranges + while (!node->basic_prep && node->simplify(true, false, false, stage, -1, false, false)) { + did_something = true; + } + } + basic_prep = true; + // The members will be laid out in the structure contiguously from left to right. + // Determine total packed size and assign offsets. Store these in the member node. + // dumpAst(NULL, "2> "); + int offset = 0; + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto node = *it; + if (is_signed) + node->is_signed = true; + int width; + if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { + auto rnode = node->children[0]; + width = (rnode->range_swapped ? rnode->range_right - rnode->range_left : + rnode->range_left - rnode->range_right) + 1; + // range nodes are now redundant + node->children.clear(); + } + else { + width = 1; + } + node->range_right = offset; + node->range_left = offset + width - 1; + node->range_valid = true; + offset += width; + } + if (!str.empty()) { + // instance rather than just a type in a typedef + // so add a wire for the packed structure + auto wnode = make_packed_struct(this, str); + current_ast_mod->children.push_back(wnode); + } + } + break; + + case AST_STRUCT_ITEM: + break; + case AST_ENUM: //log("\nENUM %s: %d child %d\n", str.c_str(), basic_prep, children[0]->basic_prep); if (!basic_prep) { @@ -884,10 +1035,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // resolve typedefs if (type == AST_TYPEDEF) { log_assert(children.size() == 1); - log_assert(children[0]->type == AST_WIRE || children[0]->type == AST_MEMORY); - while(children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) + auto type_node = children[0]; + log_assert(type_node->type == AST_WIRE || type_node->type == AST_MEMORY || type_node->type == AST_STRUCT); + while (type_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { did_something = true; - log_assert(!children[0]->is_custom_type); + } + log_assert(!type_node->is_custom_type); } // resolve types of wires @@ -895,100 +1048,57 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (is_custom_type) { log_assert(children.size() >= 1); log_assert(children[0]->type == AST_WIRETYPE); - if (!current_scope.count(children[0]->str)) - log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[0]->str.c_str()); - AstNode *resolved_type = current_scope.at(children[0]->str); - if (resolved_type->type != AST_TYPEDEF) - log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[0]->str.c_str()); - log_assert(resolved_type->children.size() == 1); - AstNode *templ = resolved_type->children[0]; + auto type_name = children[0]->str; + if (!current_scope.count(type_name)) { + log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", type_name.c_str()); + } + AstNode *resolved_type_node = current_scope.at(type_name); + if (resolved_type_node->type != AST_TYPEDEF) + log_file_error(filename, location.first_line, "`%s' does not name a type\n", type_name.c_str()); + log_assert(resolved_type_node->children.size() == 1); + AstNode *template_node = resolved_type_node->children[0]; + + // Ensure typedef itself is fully simplified + while (template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; + + if (template_node->type == AST_STRUCT) { + // replace with wire representing the packed structure + newNode = make_packed_struct(template_node, str); + current_scope[str] = this; + goto apply_newNode; + } + // Remove type reference delete children[0]; children.erase(children.begin()); - // Ensure typedef itself is fully simplified - while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; - if (type == AST_WIRE) - type = templ->type; - is_reg = templ->is_reg; - is_logic = templ->is_logic; - is_signed = templ->is_signed; - is_string = templ->is_string; - is_custom_type = templ->is_custom_type; - - range_valid = templ->range_valid; - range_swapped = templ->range_swapped; - range_left = templ->range_left; - range_right = templ->range_right; - attributes[ID::wiretype] = mkconst_str(resolved_type->str); - //check if enum - if (templ->attributes.count(ID::enum_type)){ - //get reference to enum node: - const std::string &enum_type = templ->attributes[ID::enum_type]->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_value_"; - //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() - ); - } - RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed); - enum_item_str.append(val.as_string()); - //set attribute for available val to enum item name mappings - attributes[enum_item_str] = mkconst_str(enum_item->str); - } - } + type = template_node->type; + is_reg = template_node->is_reg; + is_logic = template_node->is_logic; + is_signed = template_node->is_signed; + is_string = template_node->is_string; + is_custom_type = template_node->is_custom_type; + + range_valid = template_node->range_valid; + range_swapped = template_node->range_swapped; + range_left = template_node->range_left; + range_right = template_node->range_right; + + attributes[ID::wiretype] = mkconst_str(resolved_type_node->str); + + // if an enum then add attributes to support simulator tracing + annotateTypedEnums(template_node); // 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()); + for (int i = 0; i < GetSize(template_node->children); i++) + children.insert(children.begin() + i, template_node->children[i]->clone()); if (type == AST_MEMORY && GetSize(children) == 1) { // Single-bit memories must have [0:0] range - AstNode *rng = new AstNode(AST_RANGE); - rng->children.push_back(AstNode::mkconst_int(0, true)); - rng->children.push_back(AstNode::mkconst_int(0, true)); + AstNode *rng = make_range(0, 0); children.insert(children.begin(), rng); } - did_something = true; } log_assert(!is_custom_type); @@ -1001,29 +1111,29 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, log_assert(children[1]->type == AST_WIRETYPE); if (!current_scope.count(children[1]->str)) log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str()); - AstNode *resolved_type = current_scope.at(children[1]->str); - if (resolved_type->type != AST_TYPEDEF) + AstNode *resolved_type_node = current_scope.at(children[1]->str); + if (resolved_type_node->type != AST_TYPEDEF) log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[1]->str.c_str()); - log_assert(resolved_type->children.size() == 1); - AstNode *templ = resolved_type->children[0]; + log_assert(resolved_type_node->children.size() == 1); + AstNode *template_node = resolved_type_node->children[0]; delete children[1]; children.pop_back(); // Ensure typedef itself is fully simplified - while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; + while(template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; - if (templ->type == AST_MEMORY) + if (template_node->type == AST_MEMORY) log_file_error(filename, location.first_line, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str()); - is_signed = templ->is_signed; - is_string = templ->is_string; - is_custom_type = templ->is_custom_type; - - range_valid = templ->range_valid; - range_swapped = templ->range_swapped; - range_left = templ->range_left; - range_right = templ->range_right; - attributes[ID::wiretype] = mkconst_str(resolved_type->str); - for (auto template_child : templ->children) + is_signed = template_node->is_signed; + is_string = template_node->is_string; + is_custom_type = template_node->is_custom_type; + + range_valid = template_node->range_valid; + range_swapped = template_node->range_swapped; + range_left = template_node->range_left; + range_right = template_node->range_right; + attributes[ID::wiretype] = mkconst_str(resolved_type_node->str); + for (auto template_child : template_node->children) children.push_back(template_child->clone()); did_something = true; } @@ -1198,7 +1308,29 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } // annotate identifiers using scope resolution and create auto-wires as needed + if (type == AST_IDENTIFIER && !basic_prep) { + // check if a plausible struct member sss.mmmm + std::string sname; + if (name_has_dot(str, sname) && children.size() == 0) { + //dumpScope(); + if (current_scope.count(str) > 0) { + auto item_node = current_scope[str]; + if (item_node->type == AST_STRUCT_ITEM) { + //log("found struct item %s\n", item_node->str.c_str()); + // structure member, rewrite this node to reference the packed struct wire + auto range = make_range(item_node->range_left, item_node->range_right); + newNode = new AstNode(AST_IDENTIFIER, range); + newNode->str = sname; + //newNode->dumpAst(NULL, "* "); + newNode->basic_prep = true; + goto apply_newNode; + } + } + } + } if (type == AST_IDENTIFIER) { + //log("annotate ID %s, stage=%d cf=%d, ip=%d\n", str.c_str(), stage, const_fold, in_param); + //dumpScope(); if (current_scope.count(str) == 0) { AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod; for (auto node : current_scope_ast->children) { |