diff options
Diffstat (limited to 'frontends/ast/simplify.cc')
-rw-r--r-- | frontends/ast/simplify.cc | 164 |
1 files changed, 93 insertions, 71 deletions
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index c932e2c49..da7933d2f 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -285,10 +285,9 @@ static void save_struct_array_width(AstNode *node, int width) } -static int get_struct_array_width(AstNode *node) +static void save_struct_range_swapped(AstNode *node, bool range_swapped) { - // the stride for the array, 1 if not an array - return (node->multirange_dimensions.empty() ? 1 : node->multirange_dimensions.back()); + node->multirange_swapped.push_back(range_swapped); } @@ -318,38 +317,49 @@ static int size_packed_struct(AstNode *snode, int base_offset) // member width e.g. bit [7:0] a width = range_width(node, node->children[0]); if (node->children.size() == 2) { + // Unpacked array. Note that this is a Yosys extension; only packed data types + // and integer data types are allowed in packed structs / unions in SystemVerilog. if (node->children[1]->type == AST_RANGE) { - // unpacked array e.g. bit [63:0] a [0:3] + // Unpacked array, e.g. bit [63:0] a [0:3] auto rnode = node->children[1]; - int array_count = range_width(node, rnode); - if (array_count == 1) { - // C-type array size e.g. bit [63:0] a [4] - array_count = rnode->range_left; - } + // C-style array size, e.g. bit [63:0] a [4] + bool c_type = rnode->children.size() == 1; + int array_count = c_type ? rnode->range_left : range_width(node, rnode); + save_struct_array_width(node, array_count); + save_struct_range_swapped(node, rnode->range_swapped || c_type); save_struct_array_width(node, width); + save_struct_range_swapped(node, node->children[0]->range_swapped); width *= array_count; } else { - // array element must be single bit for a packed array + // The Yosys extension for unpacked arrays in packed structs / unions + // only supports memories, i.e. e.g. logic [7:0] a [256] - see above. struct_array_packing_error(node); } + } else { + // Vector + save_struct_array_width(node, width); + save_struct_range_swapped(node, node->children[0]->range_swapped); } // range nodes are now redundant for (AstNode *child : node->children) delete child; node->children.clear(); } - else if (node->children.size() == 1 && node->children[0]->type == AST_MULTIRANGE) { - // packed 2D array, e.g. bit [3:0][63:0] a - auto rnode = node->children[0]; - if (rnode->children.size() != 2) { - // packed arrays can only be 2D + else if (node->children.size() > 0 && node->children[0]->type == AST_MULTIRANGE) { + // Packed array, e.g. bit [3:0][63:0] a + if (node->children.size() != 1) { + // The Yosys extension for unpacked arrays in packed structs / unions + // only supports memories, i.e. e.g. logic [7:0] a [256] - see above. struct_array_packing_error(node); } - int array_count = range_width(node, rnode->children[0]); - width = range_width(node, rnode->children[1]); - save_struct_array_width(node, width); - width *= array_count; + width = 1; + for (auto rnode : node->children[0]->children) { + int rwidth = range_width(node, rnode); + save_struct_array_width(node, rwidth); + save_struct_range_swapped(node, rnode->range_swapped); + width *= rwidth; + } // range nodes are now redundant for (AstNode *child : node->children) delete child; @@ -408,55 +418,51 @@ static AstNode *multiply_by_const(AstNode *expr_node, int stride) return new AstNode(AST_MUL, expr_node, node_int(stride)); } -static AstNode *offset_indexed_range(int offset, int stride, AstNode *left_expr, AstNode *right_expr) +static AstNode *normalize_struct_index(AstNode *expr, AstNode *member_node, int dimension) { - // adjust the range expressions to add an offset into the struct - // and maybe index using an array stride - auto left = left_expr->clone(); - auto right = right_expr->clone(); - if (stride > 1) { - // newleft = (left + 1) * stride - 1 - left = new AstNode(AST_SUB, multiply_by_const(new AstNode(AST_ADD, left, node_int(1)), stride), node_int(1)); - // newright = right * stride - right = multiply_by_const(right, stride); - } - // add the offset - if (offset) { - left = new AstNode(AST_ADD, node_int(offset), left); - right = new AstNode(AST_ADD, node_int(offset), right); + expr = expr->clone(); + + if (member_node->multirange_swapped[dimension]) { + // The dimension has swapped range; swap index into the struct accordingly. + int msb = member_node->multirange_dimensions[dimension] - 1; + expr = new AstNode(AST_SUB, node_int(msb), expr); } - return new AstNode(AST_RANGE, left, right); + + return expr; } -static AstNode *make_struct_index_range(AstNode *node, AstNode *rnode, int stride, int offset) +static AstNode *struct_index_lsb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *member_node, int dimension, int &stride) { - // generate a range node to perform either bit or array indexing + stride /= member_node->multirange_dimensions[dimension]; + auto right = normalize_struct_index(rnode->children.back(), member_node, dimension); + auto offset = stride > 1 ? multiply_by_const(right, stride) : right; + return new AstNode(AST_ADD, lsb_offset, offset); +} + +static AstNode *struct_index_msb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *member_node, int dimension, int stride) +{ + log_assert(rnode->children.size() <= 2); + + // Offset to add to LSB + AstNode *offset; if (rnode->children.size() == 1) { - // index e.g. s.a[i] - return offset_indexed_range(offset, stride, rnode->children[0], rnode->children[0]); - } - else if (rnode->children.size() == 2) { - // slice e.g. s.a[i:j] - return offset_indexed_range(offset, stride, rnode->children[0], rnode->children[1]); + // Index, e.g. s.a[i] + offset = node_int(stride - 1); } else { - struct_op_error(node); + // rnode->children.size() == 2 + // Slice, e.g. s.a[i:j] + auto left = normalize_struct_index(rnode->children[0], member_node, dimension); + auto right = normalize_struct_index(rnode->children[1], member_node, dimension); + offset = new AstNode(AST_SUB, left, right); + if (stride > 1) { + // offset = (msb - lsb + 1)*stride - 1 + auto slice_width = new AstNode(AST_ADD, offset, node_int(1)); + offset = new AstNode(AST_SUB, multiply_by_const(slice_width, stride), node_int(1)); + } } -} -static AstNode *slice_range(AstNode *rnode, AstNode *snode) -{ - // apply the bit slice indicated by snode to the range rnode - log_assert(rnode->type==AST_RANGE); - auto left = rnode->children[0]; - auto right = rnode->children[1]; - log_assert(snode->type==AST_RANGE); - auto slice_left = snode->children[0]; - auto slice_right = snode->children[1]; - auto width = new AstNode(AST_SUB, slice_left->clone(), slice_right->clone()); - right = new AstNode(AST_ADD, right->clone(), slice_right->clone()); - left = new AstNode(AST_ADD, right->clone(), width); - return new AstNode(AST_RANGE, left, right); + return new AstNode(AST_ADD, lsb_offset, offset); } @@ -471,24 +477,38 @@ AstNode *AST::make_struct_member_range(AstNode *node, AstNode *member_node) // no range operations apply, return the whole width return make_range(range_left, range_right); } - int stride = get_struct_array_width(member_node); - if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { - // bit or array indexing e.g. s.a[2] or s.a[1:0] - return make_struct_index_range(node, node->children[0], stride, range_right); + + if (node->children.size() != 1) { + struct_op_error(node); } - else if (node->children.size() == 1 && node->children[0]->type == AST_MULTIRANGE) { - // multirange, i.e. bit slice after array index, e.g. s.a[i][p:q] - log_assert(stride > 1); - auto mrnode = node->children[0]; - auto element_range = make_struct_index_range(node, mrnode->children[0], stride, range_right); - // then apply bit slice range - auto range = slice_range(element_range, mrnode->children[1]); - delete element_range; - return range; + + // Range operations + auto rnode = node->children[0]; + auto lsb_offset = node_int(member_node->range_right); + int stride = range_left - range_right + 1; + size_t i = 0; + + // Calculate LSB offset for the final index / slice + if (rnode->type == AST_RANGE) { + lsb_offset = struct_index_lsb_offset(lsb_offset, rnode, member_node, i, stride); + } + else if (rnode->type == AST_MULTIRANGE) { + // Add offset for each dimension + auto mrnode = rnode; + for (i = 0; i < mrnode->children.size(); i++) { + rnode = mrnode->children[i]; + lsb_offset = struct_index_lsb_offset(lsb_offset, rnode, member_node, i, stride); + } + i--; // Step back to the final index / slice } else { struct_op_error(node); } + + // Calculate MSB offset for the final index / slice + auto msb_offset = struct_index_msb_offset(lsb_offset->clone(), rnode, member_node, i, stride); + + return new AstNode(AST_RANGE, msb_offset, lsb_offset); } static void add_members_to_scope(AstNode *snode, std::string name) @@ -1587,6 +1607,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, break; if (type == AST_GENBLOCK) break; + if (type == AST_CELLARRAY && children[i]->type == AST_CELL) + continue; if (type == AST_BLOCK && !str.empty()) break; if (type == AST_PREFIX && i >= 1) |