Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify and correct array slice assignment #4069

Merged
merged 2 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 41 additions & 70 deletions frontends/ast/simplify.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);

// All operations are carried out in a new block.
newNode = new AstNode(AST_BLOCK);

// 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));

AstNode *shamt = ref_sel;
// Calculate lsb from position.
AstNode *shift_val = pos->clone();

// 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);
// 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());
daglem marked this conversation as resolved.
Show resolved Hide resolved
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);
}
Expand Down
20 changes: 20 additions & 0 deletions tests/simple/sign_part_assign.v
Original file line number Diff line number Diff line change
@@ -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
Loading