diff --git a/README.md b/README.md index 5e5a8ec3e12..6b870e2033a 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,9 @@ Verilog Attributes and non-standard features - The ``nowrshmsk`` attribute on a register prohibits the generation of shift-and-mask type circuits for writing to bit slices of that register. +- The ``nordshift`` attribute on a register prohibits the generation of + shift type circuits for reading from bit slices of that register. + - The ``onehot`` attribute on wires mark them as one-hot state register. This is used for example for memory port sharing and set by the fsm_map pass. diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 3aa19b70687..af6531859b1 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -1445,8 +1445,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) use_const_chunk: if (children.size() != 0) { - if (children[0]->type != AST_RANGE) - input_error("Single range expected.\n"); + if (children[0]->type != AST_RANGE || !children[0]->range_valid) + input_error("Single static range expected.\n"); int source_width = id2ast->range_left - id2ast->range_right + 1; int source_offset = id2ast->range_right; int chunk_left = source_width - 1; @@ -1459,70 +1459,34 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk_right = chunk.offset; } - if (!children[0]->range_valid) { - AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero(); - AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone(); - while (left_at_zero_ast->simplify(true, false, 1, -1, false, false)) { } - while (right_at_zero_ast->simplify(true, false, 1, -1, false, false)) { } - if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); - int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; - AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ? - children[0]->children[1]->clone() : children[0]->children[0]->clone()); - fake_ast->children[0]->delete_children(); - if (member_node) - fake_ast->children[0]->attributes[ID::wiretype] = member_node->clone(); - - int fake_ast_width = 0; - bool fake_ast_sign = true; - fake_ast->children[1]->detectSignWidth(fake_ast_width, fake_ast_sign); - RTLIL::SigSpec shift_val = fake_ast->children[1]->genRTLIL(fake_ast_width, fake_ast_sign); - - if (source_offset != 0) { - shift_val = current_module->Sub(NEW_ID, shift_val, source_offset, fake_ast_sign); - fake_ast->children[1]->is_signed = true; - } - if (id2ast->range_swapped) { - shift_val = current_module->Sub(NEW_ID, RTLIL::SigSpec(source_width - width), shift_val, fake_ast_sign); - fake_ast->children[1]->is_signed = true; - } - if (GetSize(shift_val) >= 32) - fake_ast->children[1]->is_signed = true; - RTLIL::SigSpec sig = binop2rtlil(fake_ast, ID($shiftx), width, fake_ast->children[0]->genRTLIL(), shift_val); - delete left_at_zero_ast; - delete right_at_zero_ast; - delete fake_ast; - return sig; + chunk.width = children[0]->range_left - children[0]->range_right + 1; + chunk.offset += children[0]->range_right - source_offset; + if (id2ast->range_swapped) + chunk.offset = source_width - (chunk.offset + chunk.width); + if (chunk.offset > chunk_left || chunk.offset + chunk.width < chunk_right) { + if (chunk.width == 1) + log_file_warning(filename, location.first_line, "Range select out of bounds on signal `%s': Setting result bit to undef.\n", + str.c_str()); + else + log_file_warning(filename, location.first_line, "Range select [%d:%d] out of bounds on signal `%s': Setting all %d result bits to undef.\n", + children[0]->range_left, children[0]->range_right, str.c_str(), chunk.width); + chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width); } else { - chunk.width = children[0]->range_left - children[0]->range_right + 1; - chunk.offset += children[0]->range_right - source_offset; - if (id2ast->range_swapped) - chunk.offset = source_width - (chunk.offset + chunk.width); - if (chunk.offset > chunk_left || chunk.offset + chunk.width < chunk_right) { - if (chunk.width == 1) - log_file_warning(filename, location.first_line, "Range select out of bounds on signal `%s': Setting result bit to undef.\n", - str.c_str()); - else - log_file_warning(filename, location.first_line, "Range select [%d:%d] out of bounds on signal `%s': Setting all %d result bits to undef.\n", - children[0]->range_left, children[0]->range_right, str.c_str(), chunk.width); - chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width); - } else { - if (chunk.offset + chunk.width - 1 > chunk_left) { - add_undef_bits_msb = (chunk.offset + chunk.width - 1) - chunk_left; - chunk.width -= add_undef_bits_msb; - } - if (chunk.offset < chunk_right) { - add_undef_bits_lsb = chunk_right - chunk.offset; - chunk.width -= add_undef_bits_lsb; - chunk.offset += add_undef_bits_lsb; - } - if (add_undef_bits_lsb) - log_file_warning(filename, location.first_line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d LSB bits to undef.\n", - children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_lsb); - if (add_undef_bits_msb) - log_file_warning(filename, location.first_line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d MSB bits to undef.\n", - children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_msb); + if (chunk.offset + chunk.width - 1 > chunk_left) { + add_undef_bits_msb = (chunk.offset + chunk.width - 1) - chunk_left; + chunk.width -= add_undef_bits_msb; + } + if (chunk.offset < chunk_right) { + add_undef_bits_lsb = chunk_right - chunk.offset; + chunk.width -= add_undef_bits_lsb; + chunk.offset += add_undef_bits_lsb; } + if (add_undef_bits_lsb) + log_file_warning(filename, location.first_line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d LSB bits to undef.\n", + children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_lsb); + if (add_undef_bits_msb) + log_file_warning(filename, location.first_line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d MSB bits to undef.\n", + children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_msb); } } diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 64191cd7ebf..2bd1bba18fe 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -2336,6 +2336,144 @@ bool AstNode::simplify(bool const_fold, bool in_lvalue, int stage, int width_hin goto apply_newNode; } + // Rewrite dynamic indexing of rvalue packed dimensions. + if (stage > 1 && type == AST_IDENTIFIER && id2ast != NULL && id2ast->type != AST_MEMORY && !in_lvalue && + GetSize(children) == 1 && children[0]->type == AST_RANGE && !children[0]->range_valid) + { + AST::AstNode *member_node = get_struct_member(this); + int source_width = member_node ? + member_node->range_left - member_node->range_right + 1 : + id2ast->range_left - id2ast->range_right + 1; + int source_offset = id2ast->range_right; + int result_width = 1; + + AstNode *shift_expr = NULL; + AstNode *range = children[0]; + + if (range->children.size() == 1) { + shift_expr = range->children[0]->clone(); + } else { + shift_expr = range->children[1]->clone(); + if (!try_determine_range_width(range, result_width)) + input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); + } + + bool use_case_method = false; + + if (id2ast->attributes.count(ID::nordshift)) { + AstNode *node = id2ast->attributes.at(ID::nordshift); + while (node->simplify(true, false, stage, -1, false, false)) { } + if (node->type != AST_CONSTANT) + input_error("Non-constant value for `nordshift' attribute on `%s'!\n", id2ast->str.c_str()); + if (node->asAttrConst().as_bool()) + use_case_method = true; + } + + AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, node_int(result_width - 1), node_int(0))); + wire->str = stringf("$bitsel$%s$%s:%d$%d", str.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); + if (current_block) + wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false); + wire->is_logic = true; + while (wire->simplify(true, false, 1, -1, false, false)) { } + current_ast_mod->children.push_back(wire); + + AstNode *lvalue = new AstNode(AST_IDENTIFIER); + lvalue->str = wire->str; + lvalue->was_checked = true; + + AstNode *assign; + + if (use_case_method) { + // AST_CASE with an AST_COND for each possible bit slice. + + int stride = 1; + int div_stride = 1; + + // Optimization: Remove multiplication of index by stride for two-dimensional packed arrays and + // variable slices on the form dst = src[i*w +: w]. + if (!source_offset && shift_expr->type == AST_MUL && + ((shift_expr->children[0]->type == AST_CONSTANT && (int)shift_expr->children[0]->integer == result_width) || + (shift_expr->children[1]->type == AST_CONSTANT && (int)shift_expr->children[1]->integer == result_width))) + { + int var_i = shift_expr->children[0]->type == AST_CONSTANT; + AstNode *tmp = shift_expr->children[var_i]->clone(); + stride = result_width; + div_stride = stride; + delete shift_expr; + shift_expr = tmp; + } + else if (member_node) + { + // Clamp chunk to range of member within struct/union. + log_assert(!source_offset && !id2ast->range_swapped); + + // When the (* nordshift *) attribute is set, a CASE block is generated below + // to select the indexed bit slice. When a multirange array is indexed, the + // start of each possible slice is separated by the bit stride of the last + // index dimension, and we can optimize the CASE block accordingly. + // The dimension of the original array expression is saved in the 'integer' field. + int dims = integer; + stride = source_width; + for (int dim = 0; dim < dims; dim++) { + stride /= get_struct_range_width(member_node, dim); + } + } + + assign = new AstNode(AST_CASE, shift_expr); + for (int i = 0; i < source_width; i += stride) { + int start_bit = source_offset + i; + int end_bit = std::min(start_bit+result_width,source_width) - 1; + AstNode *cond = new AstNode(AST_COND, node_int(start_bit/div_stride)); + AstNode *rvalue = clone(); + rvalue->delete_children(); + if (member_node) + rvalue->attributes[ID::wiretype] = member_node->clone(); + rvalue->children.push_back(new AstNode(AST_RANGE, + node_int(end_bit), node_int(start_bit))); + cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_EQ, lvalue->clone(), rvalue))); + assign->children.push_back(cond); + } + } else { + // Shift to access the indexed bit slice. + AstNode *rvalue = clone(); + rvalue->delete_children(); + if (member_node) + rvalue->attributes[ID::wiretype] = member_node->clone(); + + if (source_offset != 0) { + // offset the shift amount by the lower bound of the dimension + shift_expr = new AstNode(AST_SUB, shift_expr, node_int(source_offset)); + } + if (id2ast->range_swapped) { + // reflect the shift amount if the dimension is swapped + shift_expr = new AstNode(AST_SUB, node_int(source_width - result_width), shift_expr); + } + + // Shift rvalue to the right so that the bit slice starts at bit 0. + assign = new AstNode(AST_ASSIGN_EQ, + lvalue->clone(), + new AstNode(AST_SHIFTX, rvalue, shift_expr)); + } + + if (current_block) { + size_t assign_idx = 0; + while (assign_idx < current_block->children.size() && current_block->children[assign_idx] != current_block_child) + assign_idx++; + log_assert(assign_idx < current_block->children.size()); + current_block->children.insert(current_block->children.begin()+assign_idx, assign); + wire->is_reg = true; + } else { + if (!use_case_method) { + assign = new AstNode(AST_BLOCK, assign); + } + AstNode *proc = new AstNode(AST_ALWAYS, assign); + current_ast_mod->children.push_back(proc); + } + + newNode = lvalue; + goto apply_newNode; + } + if (type == AST_WHILE) input_error("While loops are only allowed in constant functions!\n"); diff --git a/kernel/constids.inc b/kernel/constids.inc index 39211d0c753..1ddfdffc46f 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -135,6 +135,7 @@ X(nolatches) X(nomem2init) X(nomem2reg) X(nomeminit) +X(nordshift) X(nosync) X(nowrshmsk) X(no_rw_check) diff --git a/tests/techmap/shiftx2mux.ys b/tests/techmap/shiftx2mux.ys index 68068129730..4a59322e357 100644 --- a/tests/techmap/shiftx2mux.ys +++ b/tests/techmap/shiftx2mux.ys @@ -104,6 +104,7 @@ wire [7:0] AA = {1'bx, A}; assign Y = AA[B*2 +: 2]; endmodule EOT +proc opt wreduce equiv_opt -assert techmap