Respect \A_SIGNED for $shift

This reflects the behaviour of $shr/$shl, which sign-extend their A
operands to the size of their output, then do a logical shift (shift in
0-bits).
This commit is contained in:
Xiretza 2020-07-03 13:13:21 +02:00 committed by Marcelina Kościelnicka
parent 22765ef0a5
commit 928fd40c2e
7 changed files with 61 additions and 65 deletions

View File

@ -205,9 +205,8 @@ struct BtorWorker
if (cell->type.in(ID($xnor), ID($_XNOR_))) btor_op = "xnor"; if (cell->type.in(ID($xnor), ID($_XNOR_))) btor_op = "xnor";
log_assert(!btor_op.empty()); log_assert(!btor_op.empty());
int width = GetSize(cell->getPort(ID::Y)); int width_ay = std::max(GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::Y)));
width = std::max(width, GetSize(cell->getPort(ID::A))); int width = std::max(width_ay, GetSize(cell->getPort(ID::B)));
width = std::max(width, GetSize(cell->getPort(ID::B)));
bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false;
bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false; bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false;
@ -224,11 +223,23 @@ struct BtorWorker
int sid = get_bv_sid(width); int sid = get_bv_sid(width);
int nid; int nid;
int nid_a;
if (cell->type.in(ID($shl), ID($shr), ID($shift), ID($shiftx)) && a_signed && width_ay < width) {
// sign-extend A up to the width of Y
int nid_a_padded = get_sig_nid(cell->getPort(ID::A), width_ay, a_signed);
// zero-extend the rest
int zeroes = get_sig_nid(Const(0, width-width_ay));
nid_a = next_nid++;
btorf("%d concat %d %d %d\n", nid_a, sid, zeroes, nid_a_padded);
} else {
nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed);
}
int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed);
if (btor_op == "shift") if (btor_op == "shift")
{ {
int nid_a = get_sig_nid(cell->getPort(ID::A), width, false);
int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed);
int nid_r = next_nid++; int nid_r = next_nid++;
btorf("%d srl %d %d %d\n", nid_r, sid, nid_a, nid_b); btorf("%d srl %d %d %d\n", nid_r, sid, nid_a, nid_b);
@ -248,9 +259,6 @@ struct BtorWorker
} }
else else
{ {
int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed);
int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed);
nid = next_nid++; nid = next_nid++;
btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
} }

4
backends/btor/test_cells.sh Normal file → Executable file
View File

@ -6,7 +6,7 @@ rm -rf test_cells.tmp
mkdir -p test_cells.tmp mkdir -p test_cells.tmp
cd test_cells.tmp cd test_cells.tmp
../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$divfloor /$modfloor' ../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$divfloor /$modfloor /$shiftx'
for fn in test_*.il; do for fn in test_*.il; do
../../../yosys -p " ../../../yosys -p "
@ -19,7 +19,7 @@ for fn in test_*.il; do
hierarchy -top main hierarchy -top main
write_btor ${fn%.il}.btor write_btor ${fn%.il}.btor
" "
boolectormc -kmax 1 --trace-gen --stop-first -v ${fn%.il}.btor > ${fn%.il}.out btormc -kmax 1 --trace-gen --stop-first -v ${fn%.il}.btor > ${fn%.il}.out
if grep " SATISFIABLE" ${fn%.il}.out; then if grep " SATISFIABLE" ${fn%.il}.out; then
echo "Check failed for ${fn%.il}." echo "Check failed for ${fn%.il}."
exit 1 exit 1

View File

@ -750,21 +750,19 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf(" = "); f << stringf(" = ");
if (cell->getParam(ID::B_SIGNED).as_bool()) if (cell->getParam(ID::B_SIGNED).as_bool())
{ {
f << stringf("$signed("); dump_cell_expr_port(f, cell, "B", true);
dump_sigspec(f, cell->getPort(ID::B));
f << stringf(")");
f << stringf(" < 0 ? "); f << stringf(" < 0 ? ");
dump_sigspec(f, cell->getPort(ID::A)); dump_cell_expr_port(f, cell, "A", true);
f << stringf(" << - "); f << stringf(" << - ");
dump_sigspec(f, cell->getPort(ID::B)); dump_sigspec(f, cell->getPort(ID::B));
f << stringf(" : "); f << stringf(" : ");
dump_sigspec(f, cell->getPort(ID::A)); dump_cell_expr_port(f, cell, "A", true);
f << stringf(" >> "); f << stringf(" >> ");
dump_sigspec(f, cell->getPort(ID::B)); dump_sigspec(f, cell->getPort(ID::B));
} }
else else
{ {
dump_sigspec(f, cell->getPort(ID::A)); dump_cell_expr_port(f, cell, "A", true);
f << stringf(" >> "); f << stringf(" >> ");
dump_sigspec(f, cell->getPort(ID::B)); dump_sigspec(f, cell->getPort(ID::B));
} }

View File

@ -275,10 +275,15 @@ RTLIL::Const RTLIL::const_logic_or(const RTLIL::Const &arg1, const RTLIL::Const
return result; return result;
} }
static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool sign_ext, int direction, int result_len) // Shift `arg1` by `arg2` bits.
// If `direction` is +1, `arg1` is shifted right by `arg2` bits; if `direction` is -1, `arg1` is shifted left by `arg2` bits.
// If `signed2` is true, `arg2` is interpreted as a signed integer; a negative `arg2` will cause a shift in the opposite direction.
// Any required bits outside the bounds of `arg1` are padded with `vacant_bits` unless `sign_ext` is true, in which case any bits outside the left
// bounds are filled with the leftmost bit of `arg1` (arithmetic shift).
static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool sign_ext, bool signed2, int direction, int result_len, RTLIL::State vacant_bits = RTLIL::State::S0)
{ {
int undef_bit_pos = -1; int undef_bit_pos = -1;
BigInteger offset = const2big(arg2, false, undef_bit_pos) * direction; BigInteger offset = const2big(arg2, signed2, undef_bit_pos) * direction;
if (result_len < 0) if (result_len < 0)
result_len = arg1.bits.size(); result_len = arg1.bits.size();
@ -290,9 +295,9 @@ static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Co
for (int i = 0; i < result_len; i++) { for (int i = 0; i < result_len; i++) {
BigInteger pos = BigInteger(i) + offset; BigInteger pos = BigInteger(i) + offset;
if (pos < 0) if (pos < 0)
result.bits[i] = RTLIL::State::S0; result.bits[i] = vacant_bits;
else if (pos >= BigInteger(int(arg1.bits.size()))) else if (pos >= BigInteger(int(arg1.bits.size())))
result.bits[i] = sign_ext ? arg1.bits.back() : RTLIL::State::S0; result.bits[i] = sign_ext ? arg1.bits.back() : vacant_bits;
else else
result.bits[i] = arg1.bits[pos.toInt()]; result.bits[i] = arg1.bits[pos.toInt()];
} }
@ -304,61 +309,36 @@ RTLIL::Const RTLIL::const_shl(const RTLIL::Const &arg1, const RTLIL::Const &arg2
{ {
RTLIL::Const arg1_ext = arg1; RTLIL::Const arg1_ext = arg1;
extend_u0(arg1_ext, result_len, signed1); extend_u0(arg1_ext, result_len, signed1);
return const_shift_worker(arg1_ext, arg2, false, -1, result_len); return const_shift_worker(arg1_ext, arg2, false, false, -1, result_len);
} }
RTLIL::Const RTLIL::const_shr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len) RTLIL::Const RTLIL::const_shr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len)
{ {
RTLIL::Const arg1_ext = arg1; RTLIL::Const arg1_ext = arg1;
extend_u0(arg1_ext, max(result_len, GetSize(arg1)), signed1); extend_u0(arg1_ext, max(result_len, GetSize(arg1)), signed1);
return const_shift_worker(arg1_ext, arg2, false, +1, result_len); return const_shift_worker(arg1_ext, arg2, false, false, +1, result_len);
} }
RTLIL::Const RTLIL::const_sshl(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) RTLIL::Const RTLIL::const_sshl(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len)
{ {
if (!signed1) return const_shift_worker(arg1, arg2, signed1, false, -1, result_len);
return const_shl(arg1, arg2, signed1, signed2, result_len);
return const_shift_worker(arg1, arg2, true, -1, result_len);
} }
RTLIL::Const RTLIL::const_sshr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) RTLIL::Const RTLIL::const_sshr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len)
{ {
if (!signed1) return const_shift_worker(arg1, arg2, signed1, false, +1, result_len);
return const_shr(arg1, arg2, signed1, signed2, result_len);
return const_shift_worker(arg1, arg2, true, +1, result_len);
}
static RTLIL::Const const_shift_shiftx(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool signed2, int result_len, RTLIL::State other_bits)
{
int undef_bit_pos = -1;
BigInteger offset = const2big(arg2, signed2, undef_bit_pos);
if (result_len < 0)
result_len = arg1.bits.size();
RTLIL::Const result(RTLIL::State::Sx, result_len);
if (undef_bit_pos >= 0)
return result;
for (int i = 0; i < result_len; i++) {
BigInteger pos = BigInteger(i) + offset;
if (pos < 0 || pos >= BigInteger(int(arg1.bits.size())))
result.bits[i] = other_bits;
else
result.bits[i] = arg1.bits[pos.toInt()];
}
return result;
} }
RTLIL::Const RTLIL::const_shift(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) RTLIL::Const RTLIL::const_shift(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
{ {
return const_shift_shiftx(arg1, arg2, signed1, signed2, result_len, RTLIL::State::S0); RTLIL::Const arg1_ext = arg1;
extend_u0(arg1_ext, max(result_len, GetSize(arg1)), signed1);
return const_shift_worker(arg1_ext, arg2, false, signed2, +1, result_len);
} }
RTLIL::Const RTLIL::const_shiftx(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) RTLIL::Const RTLIL::const_shiftx(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool signed2, int result_len)
{ {
return const_shift_shiftx(arg1, arg2, signed1, signed2, result_len, RTLIL::State::Sx); return const_shift_worker(arg1, arg2, false, signed2, +1, result_len, RTLIL::State::Sx);
} }
RTLIL::Const RTLIL::const_lt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) RTLIL::Const RTLIL::const_lt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)

View File

@ -522,7 +522,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
int extend_bit = ez->CONST_FALSE; int extend_bit = ez->CONST_FALSE;
if (!cell->type.in(ID($shift), ID($shiftx)) && cell->parameters[ID::A_SIGNED].as_bool()) if (cell->parameters[ID::A_SIGNED].as_bool())
extend_bit = a.back(); extend_bit = a.back();
while (y.size() < a.size()) while (y.size() < a.size())
@ -555,7 +555,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
std::vector<int> undef_a_shifted; std::vector<int> undef_a_shifted;
extend_bit = cell->type == ID($shiftx) ? ez->CONST_TRUE : ez->CONST_FALSE; extend_bit = cell->type == ID($shiftx) ? ez->CONST_TRUE : ez->CONST_FALSE;
if (!cell->type.in(ID($shift), ID($shiftx)) && cell->parameters[ID::A_SIGNED].as_bool()) if (cell->parameters[ID::A_SIGNED].as_bool())
extend_bit = undef_a.back(); extend_bit = undef_a.back();
while (undef_y.size() < undef_a.size()) while (undef_y.size() < undef_a.size())

View File

@ -480,10 +480,18 @@ input [B_WIDTH-1:0] B;
output [Y_WIDTH-1:0] Y; output [Y_WIDTH-1:0] Y;
generate generate
if (B_SIGNED) begin:BLOCK1 if (A_SIGNED) begin:BLOCK1
assign Y = $signed(B) < 0 ? A << -B : A >> B; if (B_SIGNED) begin:BLOCK2
end else begin:BLOCK2 assign Y = $signed(B) < 0 ? $signed(A) << -B : $signed(A) >> B;
assign Y = A >> B; end else begin:BLOCK3
assign Y = $signed(A) >> B;
end
end else begin:BLOCK4
if (B_SIGNED) begin:BLOCK5
assign Y = $signed(B) < 0 ? A << -B : A >> B;
end else begin:BLOCK6
assign Y = A >> B;
end
end end
endgenerate endgenerate

View File

@ -141,6 +141,7 @@ module _90_shift_shiftx (A, B, Y);
parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0; parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0;
localparam extbit = _TECHMAP_CELLTYPE_ == "$shift" ? 1'b0 : 1'bx; localparam extbit = _TECHMAP_CELLTYPE_ == "$shift" ? 1'b0 : 1'bx;
wire a_padding = _TECHMAP_CELLTYPE_ == "$shiftx" ? extbit : (A_SIGNED ? A[A_WIDTH-1] : 1'b0);
generate generate
`ifndef NO_LSB_FIRST_SHIFT_SHIFTX `ifndef NO_LSB_FIRST_SHIFT_SHIFTX
@ -160,7 +161,7 @@ module _90_shift_shiftx (A, B, Y);
localparam entries = (A_WIDTH+Y_WIDTH-1)/Y_WIDTH2; localparam entries = (A_WIDTH+Y_WIDTH-1)/Y_WIDTH2;
localparam len = Y_WIDTH2 * ((entries+1)/2); localparam len = Y_WIDTH2 * ((entries+1)/2);
wire [len-1:0] AA; wire [len-1:0] AA;
wire [(A_WIDTH+Y_WIDTH2+Y_WIDTH-1)-1:0] Apad = {{(Y_WIDTH2+Y_WIDTH-1){extbit}}, A}; wire [(A_WIDTH+Y_WIDTH2+Y_WIDTH-1)-1:0] Apad = {{(Y_WIDTH2+Y_WIDTH-1){a_padding}}, A};
genvar i; genvar i;
for (i = 0; i < A_WIDTH; i=i+Y_WIDTH2*2) for (i = 0; i < A_WIDTH; i=i+Y_WIDTH2*2)
assign AA[i/2 +: Y_WIDTH2] = B[CLOG2_Y_WIDTH] ? Apad[i+Y_WIDTH2 +: Y_WIDTH2] : Apad[i +: Y_WIDTH2]; assign AA[i/2 +: Y_WIDTH2] = B[CLOG2_Y_WIDTH] ? Apad[i+Y_WIDTH2 +: Y_WIDTH2] : Apad[i +: Y_WIDTH2];
@ -187,7 +188,8 @@ module _90_shift_shiftx (A, B, Y);
always @* begin always @* begin
overflow = 0; overflow = 0;
buffer = {WIDTH{extbit}}; buffer = {WIDTH{extbit}};
buffer[`MAX(A_WIDTH, Y_WIDTH)-1:0] = A; buffer[Y_WIDTH-1:0] = {Y_WIDTH{a_padding}};
buffer[A_WIDTH-1:0] = A;
if (B_WIDTH > BB_WIDTH) begin if (B_WIDTH > BB_WIDTH) begin
if (B_SIGNED) begin if (B_SIGNED) begin