From dbeba71f5445934b28a2e149ea7e0e1a98b26a2c Mon Sep 17 00:00:00 2001 From: Dag Lem Date: Mon, 13 Mar 2023 21:29:11 +0100 Subject: [PATCH] Added nordshift attribute nordshift on a register yields generation of muxes instead of shift circuits for dynamic rvalue indexing, akin to nowrshmsk for lvalue indexing. To facilitate this, the AST transformations for rvalue indexing are moved from genrtlil.cc to simplify.cc, bringing them in line with transformations for lvalue indexing. --- README.md | 3 + frontends/ast/genrtlil.cc | 92 ++++++++---------------- frontends/ast/simplify.cc | 138 ++++++++++++++++++++++++++++++++++++ kernel/constids.inc | 1 + tests/techmap/shiftx2mux.ys | 1 + 5 files changed, 171 insertions(+), 64 deletions(-) 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