mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #4069 from daglem/simplify-array-slice-assignment
Simplify and correct array slice assignment
This commit is contained in:
commit
eeadbb583e
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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…
Reference in New Issue