diff --git a/README.md b/README.md index 69a227b7fb5..e465b9df537 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 net or variable prohibits the generation of + shift type circuits for reading bit slices from that data object. + - 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 0bae0f67374..ed69d4c0941 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -1449,8 +1449,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } // simply return the corresponding RTLIL::SigSpec for an AST_IDENTIFIER node - // for identifiers with dynamic bit ranges (e.g. "foo[bar]" or "foo[bar+3:bar]") a - // shifter cell is created and the output signal of this cell is returned case AST_IDENTIFIER: { RTLIL::Wire *wire = NULL; @@ -1529,8 +1527,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; @@ -1543,70 +1541,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, 1, -1, false)) { } - while (right_at_zero_ast->simplify(true, 1, -1, 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]->set_attribute(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 f313e3360b8..9741bd98062 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1387,6 +1387,8 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin case AST_ASSIGN: while (!children[0]->basic_prep && children[0]->simplify(false, stage, -1, false) == true) did_something = true; + if (type == AST_ASSIGN && children[0]->type == AST_IDENTIFIER && children[0]->children.size() > 0 && !children[0]->children[0]->range_valid) + log_error("Non-constant range in continuous assignment to %s at %s.\n", children[0]->str.c_str(), loc_string().c_str()); while (!children[1]->basic_prep && children[1]->simplify(false, stage, -1, false) == true) did_something = true; children[0]->detectSignWidth(backup_width_hint, backup_sign_hint); @@ -2337,6 +2339,184 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin goto apply_newNode; } + // Rewrite dynamic indexing of rvalue packed dimensions. + if (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 wire_width = member_node ? + member_node->range_left - member_node->range_right + 1 : + id2ast->range_left - id2ast->range_right + 1; + int wire_offset = id2ast->range_right; + + AstNode *range = children[0]; + int result_width; + if (!try_determine_range_width(range, result_width)) + input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); + AstNode *shift_expr = range->children.size() >= 2 ? range->children[1]->clone() : range->children[0]->clone(); + + int shift_expr_width_hint; + bool shift_expr_sign_hint; + shift_expr->detectSignWidth(shift_expr_width_hint, shift_expr_sign_hint); + + bool use_case_method = id2ast->get_bool_attribute(ID::nordshift); + + if (use_case_method) { + // AST_CASE with an AST_COND for each possible bit slice. + + int stride = 1; + long long bitno_div = stride; + int shift_expr_width_max = shift_expr_width_hint; + + if (member_node) { + // Clamp chunk to range of member within struct/union. + log_assert(wire_offset == 0 && !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 = wire_width; + for (int dim = 0; dim < dims; dim++) { + stride /= get_struct_range_width(member_node, dim); + } + bitno_div = stride; + } else { + // Extract (index)*(width) from non_opt_range pattern (@selfsz@((index)*(width)))+(0). + AstNode *lsb_expr = + shift_expr->type == AST_ADD && shift_expr->children[0]->type == AST_SELFSZ && + shift_expr->children[1]->type == AST_CONSTANT && shift_expr->children[1]->integer == 0 ? + shift_expr->children[0]->children[0] : + shift_expr; + + // Extract stride from indexing of two-dimensional packed arrays and + // variable slices on the form src[i*stride +: width]. + if (lsb_expr->type == AST_MUL && + (lsb_expr->children[0]->type == AST_CONSTANT || + lsb_expr->children[1]->type == AST_CONSTANT)) + { + int stride_ix = lsb_expr->children[1]->type == AST_CONSTANT; + stride = (int)lsb_expr->children[stride_ix]->integer; + bitno_div = stride != 0 ? stride : 1; + + // Check whether i*stride can overflow. + int i_width; + bool i_sign; + lsb_expr->children[1 - stride_ix]->detectSignWidth(i_width, i_sign); + int stride_width; + bool stride_sign; + lsb_expr->children[stride_ix]->detectSignWidth(stride_width, stride_sign); + shift_expr_width_max = std::max(i_width, stride_width); + // Stride width calculated from actual stride value. + stride_width = std::ceil(std::log2(std::abs(stride))); + + if (i_width + stride_width > shift_expr_width_max) { + // For (truncated) i*stride to be within the range of dst, the following must hold: + // i*stride ≡ bitno (mod shift_mod), i.e. + // i*stride = k*shift_mod + bitno + // + // The Diophantine equation on the form ax + by = c: + // stride*i - shift_mod*k = bitno + // has solutions iff c is a multiple of d = gcd(a, b), i.e. + // bitno mod gcd(stride, shift_mod) = 0 + // + // long long is at least 64 bits in C++11 + long long shift_mod = 1ll << (shift_expr_width_max - shift_expr_sign_hint); + // std::gcd requires C++17 + // bitno_div = std::gcd(stride, shift_mod); + bitno_div = gcd((long long)stride, shift_mod); + } + } + } + + // long long is at least 64 bits in C++11 + long long max_offset = (1ll << (shift_expr_width_max - shift_expr_sign_hint)) - 1; + long long min_offset = shift_expr_sign_hint ? -(1ll << (shift_expr_width_max - 1)) : 0; + + if (!shift_expr_sign_hint && max_offset >= wire_offset + wire_width) { + // When an unsigned address expression can go beyond the address range, + // we must handle potential overflows causing the lower bits of the wire + // to be selected. + min_offset = -((max_offset + 1) >> 1); + } + + // Temporary register to replace the current rvalue; used as lvalue in AST_COND assignments. + AstNode *lvalue = mktemp_logic("$bitsel$"+str+"$", current_ast_mod, current_block, result_width - 1, 0, false); + + AstNode *assign = new AstNode(AST_CASE, shift_expr); + for (int i = 1 - result_width; i < wire_width; i++) { + // Out of range indexes are handled in genrtlil.cc + int start_bit = wire_offset + i; + int end_bit = start_bit + result_width - 1; + // Check whether the current index can be generated by shift_expr. + if (start_bit < min_offset || start_bit > max_offset) + continue; + if (start_bit%bitno_div != 0 || (stride == 0 && start_bit != 0)) + continue; + AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, shift_expr_sign_hint, shift_expr_width_max)); + AstNode *rvalue = clone(); + rvalue->delete_children(); + if (member_node) + rvalue->set_attribute(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); + } + + // Default to x bits for out of range addresses. + std::vector x_const(result_width, RTLIL::State::Sx); + assign->children.push_back(new AstNode(AST_COND, + new AstNode(AST_DEFAULT), + new AstNode(AST_BLOCK, + new AstNode(AST_ASSIGN_EQ, + lvalue->clone(), AstNode::mkconst_bits(x_const, false))))); + + 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); + } else { + AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK, assign)); + current_ast_mod->children.push_back(proc); + } + + newNode = lvalue; + } else { + // Shift to access the indexed bit slice. + // This is adapted from old code in genrtlil.cc + AstNode *rvalue = clone(); + rvalue->delete_children(); + if (member_node) + rvalue->set_attribute(ID::wiretype, member_node->clone()); + + if (wire_offset != 0) { + // Offset the shift amount by the lower bound of the dimension. + shift_expr = new AstNode(AST_SUB, shift_expr, node_int(wire_offset)); + } + if (id2ast->range_swapped) { + // Reflect the shift amount if the dimension is swapped. + shift_expr = new AstNode(AST_SUB, node_int(wire_width - result_width), shift_expr); + } + + if (!shift_expr_sign_hint && (wire_offset != 0 || id2ast->range_swapped || shift_expr_width_hint >= 32)) { + // Handle potential unsigned overflows, causing the lower bits of the wire to be selected. + shift_expr = new AstNode(AST_TO_SIGNED, shift_expr); + } + + // Shift rvalue to the right so that the bit slice starts at bit 0. + newNode = new AstNode(AST_CAST_SIZE, + node_int(result_width), + new AstNode(AST_SHIFTX, rvalue, shift_expr)); + } + + 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 480e2afc6fa..ec01e63121c 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -138,6 +138,7 @@ X(nolatches) X(nomem2init) X(nomem2reg) X(nomeminit) +X(nordshift) X(nosync) X(nowrshmsk) X(no_ram)