aboutsummaryrefslogtreecommitdiffstats
path: root/frontends/ast/simplify.cc
diff options
context:
space:
mode:
authorPeter Crozier <peter@crozier.com>2020-05-08 14:40:49 +0100
committerPeter Crozier <peter@crozier.com>2020-05-08 14:40:49 +0100
commit0b6b47ca670b9219bcb81ab7d3599267c2ef7571 (patch)
tree7f185ed9add86d52c5f55828e07b190420c77511 /frontends/ast/simplify.cc
parentaafaeb66dfd839b8223059884d2741dadc9e2d92 (diff)
downloadyosys-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.cc334
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) {