Merge pull request #4069 from daglem/simplify-array-slice-assignment

Simplify and correct array slice assignment
This commit is contained in:
Martin Povišer 2024-01-11 11:41:34 +01:00 committed by GitHub
commit eeadbb583e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 70 deletions

View File

@ -2966,96 +2966,67 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
}
} else {
// mask and shift operations
AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(wire_width-1, true), mkconst_int(0, true)));
wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
wire_mask->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
wire_mask->is_logic = true;
while (wire_mask->simplify(true, 1, -1, false)) { }
current_ast_mod->children.push_back(wire_mask);
AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(wire_width-1, true), mkconst_int(0, true)));
wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
wire_data->is_logic = true;
while (wire_data->simplify(true, 1, -1, false)) { }
current_ast_mod->children.push_back(wire_data);
int shamt_width_hint = -1;
bool shamt_sign_hint = true;
shift_expr->detectSignWidth(shamt_width_hint, shamt_sign_hint);
AstNode *wire_sel = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(shamt_width_hint-1, true), mkconst_int(0, true)));
wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++);
wire_sel->set_attribute(ID::nosync, AstNode::mkconst_int(1, false));
wire_sel->is_logic = true;
wire_sel->is_signed = shamt_sign_hint;
while (wire_sel->simplify(true, 1, -1, false)) { }
current_ast_mod->children.push_back(wire_sel);
did_something = true;
newNode = new AstNode(AST_BLOCK);
// dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb)
AstNode *lvalue = children[0]->clone();
lvalue->delete_children();
if (member_node)
lvalue->set_attribute(ID::wiretype, member_node->clone());
AstNode *ref_mask = new AstNode(AST_IDENTIFIER);
ref_mask->str = wire_mask->str;
ref_mask->id2ast = wire_mask;
ref_mask->was_checked = true;
AstNode *ref_data = new AstNode(AST_IDENTIFIER);
ref_data->str = wire_data->str;
ref_data->id2ast = wire_data;
ref_data->was_checked = true;
AstNode *ref_sel = new AstNode(AST_IDENTIFIER);
ref_sel->str = wire_sel->str;
ref_sel->id2ast = wire_sel;
ref_sel->was_checked = true;
AstNode *old_data = lvalue->clone();
if (type == AST_ASSIGN_LE)
old_data->lookahead = true;
AstNode *s = new AstNode(AST_ASSIGN_EQ, ref_sel->clone(), shift_expr);
newNode->children.push_back(s);
int shift_width_hint;
bool shift_sign_hint;
shift_expr->detectSignWidth(shift_width_hint, shift_sign_hint);
AstNode *shamt = ref_sel;
// All operations are carried out in a new block.
newNode = new AstNode(AST_BLOCK);
// convert to signed while preserving the sign and value
shamt = new AstNode(AST_CAST_SIZE, mkconst_int(shamt_width_hint + 1, true), shamt);
shamt = new AstNode(AST_TO_SIGNED, shamt);
// Temporary register holding the result of the bit- or part-select position expression.
AstNode *pos = mktemp_logic("$bitselwrite$pos$", current_ast_mod, true, shift_width_hint - 1, 0, shift_sign_hint);
newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, pos, shift_expr));
// Calculate lsb from position.
AstNode *shift_val = pos->clone();
// If the expression is signed, we must add an extra bit for possible negation of the most negative number.
// If the expression is unsigned, we must add an extra bit for sign.
shift_val = new AstNode(AST_CAST_SIZE, mkconst_int(shift_width_hint + 1, true), shift_val);
if (!shift_sign_hint)
shift_val = new AstNode(AST_TO_SIGNED, shift_val);
// offset the shift amount by the lower bound of the dimension
int start_bit = wire_offset;
shamt = new AstNode(AST_SUB, shamt, mkconst_int(start_bit, true));
if (wire_offset != 0)
shift_val = new AstNode(AST_SUB, shift_val, mkconst_int(wire_offset, true));
// reflect the shift amount if the dimension is swapped
if (children[0]->id2ast->range_swapped)
shamt = new AstNode(AST_SUB, mkconst_int(wire_width - result_width, true), shamt);
shift_val = new AstNode(AST_SUB, mkconst_int(wire_width - result_width, true), shift_val);
// AST_SHIFT uses negative amounts for shifting left
shamt = new AstNode(AST_NEG, shamt);
shift_val = new AstNode(AST_NEG, shift_val);
AstNode *t;
t = mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false);
t = new AstNode(AST_SHIFT, t, shamt->clone());
t = new AstNode(AST_ASSIGN_EQ, ref_mask->clone(), t);
newNode->children.push_back(t);
t = new AstNode(AST_BIT_AND, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), children[1]->clone());
t = new AstNode(AST_SHIFT, t, shamt);
t = new AstNode(AST_ASSIGN_EQ, ref_data->clone(), t);
newNode->children.push_back(t);
t = new AstNode(AST_BIT_AND, old_data, new AstNode(AST_BIT_NOT, ref_mask));
t = new AstNode(AST_BIT_OR, t, ref_data);
t = new AstNode(type, lvalue, t);
newNode->children.push_back(t);
// dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb)
did_something = true;
AstNode *bitmask = mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false);
newNode->children.push_back(
new AstNode(type,
lvalue,
new AstNode(AST_BIT_OR,
new AstNode(AST_BIT_AND,
old_data,
new AstNode(AST_BIT_NOT,
new AstNode(AST_SHIFT,
bitmask,
shift_val->clone()))),
new AstNode(AST_SHIFT,
new AstNode(AST_TO_UNSIGNED,
new AstNode(AST_CAST_SIZE,
mkconst_int(result_width, true),
children[1]->clone())),
shift_val))));
newNode->fixup_hierarchy_flags(true);
}

View File

@ -0,0 +1,20 @@
module test (
offset_i,
data_o,
data_ref_o
);
input wire [ 2:0] offset_i;
output reg [15:0] data_o;
output reg [15:0] data_ref_o;
always @(*) begin
// defaults
data_o = '0;
data_ref_o = '0;
// partial assigns
data_ref_o[offset_i+:4] = 4'b1111; // unsigned
data_o[offset_i+:4] = 1'sb1; // sign extension to 4'b1111
end
endmodule