Skip to content

Verilog: typechecking for relations #667

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

Merged
merged 1 commit into from
Sep 9, 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
2 changes: 1 addition & 1 deletion regression/ebmc/example1/example1.sv
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ module main(input a, input b);

my_add adder(a, b, result);

assert final (a+b==result);
assert final (2'(a)+b==result);

endmodule
2 changes: 1 addition & 1 deletion regression/verilog/SVA/immediate3.desc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
CORE
immediate3.sv
--bound 0
^\[full_adder\.assert\.1\] always \{ full_adder\.carry, full_adder\.sum \} == full_adder\.a \+ full_adder\.b \+ full_adder\.c: PROVED up to bound 0$
^\[full_adder\.assert\.1\] always \{ full_adder\.carry, full_adder\.sum \} == \{ 1'b0, full_adder\.a \} \+ full_adder\.b \+ full_adder\.c: PROVED up to bound 0$
^EXIT=0$
^SIGNAL=0$
--
Expand Down
2 changes: 1 addition & 1 deletion regression/verilog/SVA/immediate3.sv
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ module full_adder(input a, b, c, output sum, carry);
assign carry = a & b | (a ^ b) & c;

always @(*)
assert final({carry, sum} == a + b + c);
assert final({carry, sum} == {1'b0, a} + b + c);

endmodule
2 changes: 1 addition & 1 deletion regression/verilog/SVA/static_final1.desc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
CORE
static_final1.sv
--bound 0
^\[full_adder\.p0\] always \{ full_adder\.carry, full_adder\.sum \} == full_adder\.a \+ full_adder\.b \+ full_adder\.c: PROVED up to bound 0$
^\[full_adder\.p0\] always \{ full_adder\.carry, full_adder\.sum \} == \{ 1'b0, full_adder\.a \} \+ full_adder\.b \+ full_adder\.c: PROVED up to bound 0$
^EXIT=0$
^SIGNAL=0$
--
Expand Down
2 changes: 1 addition & 1 deletion regression/verilog/SVA/static_final1.sv
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ module full_adder(input a, b, c, output sum, carry);

// 1800-2017
// 16.4.3 Deferred assertions outside procedural code
p0: assert final ({carry, sum} == a + b + c);
p0: assert final ({carry, sum} == {1'b0, a} + b + c);

endmodule
2 changes: 1 addition & 1 deletion regression/verilog/expressions/concatenation1.v
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module main(in1, in2);
always @in1
out = { in2, in1 };

always assert property1: out == ((in2<<16)|in1);
always assert property1: out == (({16'b0, in2}<<16)|in1);

always assert property2: {4'b1001,4'b1011}==8'b10011011;

Expand Down
4 changes: 2 additions & 2 deletions regression/verilog/expressions/equality1.desc
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ equality1.v
^\[.*\] always 32'b0000000000000000000000000000000z == 20 === 32'b0000000000000000000000000000000x: PROVED up to bound 0$
^\[.*\] always 32'b0000000000000000000000000000000x != 10 === 32'b0000000000000000000000000000000x: PROVED up to bound 0$
^\[.*\] always 32'b0000000000000000000000000000000z != 20 === 32'b0000000000000000000000000000000x: PROVED up to bound 0$
^\[.*\] always 2'b11 == 2'b11 === 0: REFUTED$
^\[.*\] always 1'sb1 == 2'b11 === 0: PROVED up to bound 0$
^\[.*\] always 2'sb11 == 2'sb11 === 1: PROVED up to bound 0$
^EXIT=10$
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
Expand Down
4 changes: 2 additions & 2 deletions regression/verilog/expressions/equality2.desc
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ equality2.v
^\[.*\] always 32'b0000000000000000000000000000000x === 1 == 0: PROVED up to bound 0$
^\[.*\] always 32'b0000000000000000000000000000000z === 1 == 0: PROVED up to bound 0$
^\[.*\] always 1 === 1 == 1: PROVED up to bound 0$
^\[.*\] always 3'b011 === 3'b111 == 1: REFUTED$
^\[.*\] always 3'b011 === 2'sb11 == 1: PROVED up to bound 0$
^\[.*\] always 3'sb111 === 3'sb111 == 1: PROVED up to bound 0$
^EXIT=10$
^EXIT=0$
^SIGNAL=0$
--
^warning: ignoring
Expand Down
2 changes: 1 addition & 1 deletion regression/verilog/expressions/replication1.v
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module main(in);
out = { 4 { in } };

always assert property1:
out==(in | (in<<8) | (in<<16) | (in<<24));
out==({24'b0, in} | (in<<8) | (in<<16) | (in<<24));

// 1-replication
always assert property2:
Expand Down
129 changes: 111 additions & 18 deletions src/verilog/verilog_typecheck_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2118,6 +2118,33 @@ void verilog_typecheck_exprt::convert_range(

/*******************************************************************\

Function: verilog_typecheck_exprt::enum_decay

Inputs:

Outputs:

Purpose:

\*******************************************************************/

typet verilog_typecheck_exprt::enum_decay(const typet &type)
{
// Verilog enum types decay to their base type when used in relational
// or arithmetic expressions.
if(type.get(ID_C_verilog_type) == ID_verilog_enum)
{
typet result = type;
result.remove(ID_C_verilog_type);
result.remove(ID_C_identifier);
return result;
}
else
return type;
}

/*******************************************************************\

Function: verilog_typecheck_exprt::tc_binary_expr

Inputs:
Expand All @@ -2132,20 +2159,7 @@ void verilog_typecheck_exprt::tc_binary_expr(
const exprt &expr,
exprt &op0, exprt &op1)
{
// Verilog enum types decay to their base type when used in relational
// or arithmetic expressions.
auto enum_decay = [](const typet &type) {
if(type.get(ID_C_verilog_type) == ID_verilog_enum)
{
typet result = type;
result.remove(ID_C_verilog_type);
result.remove(ID_C_identifier);
return result;
}
else
return type;
};

// follows 1800-2017 11.8.2
const typet new_type =
max_type(enum_decay(op0.type()), enum_decay(op1.type()));

Expand All @@ -2163,6 +2177,85 @@ void verilog_typecheck_exprt::tc_binary_expr(

/*******************************************************************\

Function: zero_extend

Inputs:

Outputs:

Purpose:

\*******************************************************************/

static exprt zero_extend(const exprt &expr, const typet &type)
{
auto old_width = expr.type().id() == ID_bool
? 1
: to_bitvector_type(expr.type()).get_width();

// first make unsigned
typet tmp_type;

if(type.id() == ID_unsignedbv)
tmp_type = unsignedbv_typet{old_width};
else if(type.id() == ID_verilog_unsignedbv)
tmp_type = verilog_unsignedbv_typet{old_width};
else
PRECONDITION(false);

return typecast_exprt::conditional_cast(
typecast_exprt::conditional_cast(expr, tmp_type), type);
}

/*******************************************************************\

Function: verilog_typecheck_exprt::typecheck_relation

Inputs:

Outputs:

Purpose:

\*******************************************************************/

void verilog_typecheck_exprt::typecheck_relation(binary_exprt &expr)
{
auto &lhs = expr.lhs();
auto &rhs = expr.rhs();

// Relations are special-cased in 1800-2017 11.8.2.
const typet new_type =
max_type(enum_decay(lhs.type()), enum_decay(rhs.type()));

if(new_type.is_nil())
{
throw errort().with_location(expr.source_location())
<< "expected operands of compatible type but got:\n"
<< " " << to_string(lhs.type()) << '\n'
<< " " << to_string(rhs.type());
}

// If both operands are signed, both are sign-extended to the max width.
// Otherwise, both are zero-extended to the max width.
// In particular, signed operands are then _not_ sign extended,
// which a typecast would do.
if(new_type.id() == ID_verilog_unsignedbv || new_type.id() == ID_unsignedbv)
{
// zero extend both operands
lhs = zero_extend(lhs, new_type);
rhs = zero_extend(rhs, new_type);
}
else
{
// convert
implicit_typecast(lhs, new_type);
implicit_typecast(rhs, new_type);
}
}

/*******************************************************************\

Function: verilog_typecheck_exprt::max_type

Inputs:
Expand Down Expand Up @@ -2561,7 +2654,7 @@ exprt verilog_typecheck_exprt::convert_binary_expr(binary_exprt expr)
Forall_operands(it, expr)
convert_expr(*it);

tc_binary_expr(expr);
typecheck_relation(expr);

return std::move(expr);
}
Expand All @@ -2573,7 +2666,7 @@ exprt verilog_typecheck_exprt::convert_binary_expr(binary_exprt expr)
Forall_operands(it, expr)
convert_expr(*it);

tc_binary_expr(expr);
typecheck_relation(expr);

// This returns 'x' if either of the operands contains x or z.
if(
Expand Down Expand Up @@ -2610,7 +2703,7 @@ exprt verilog_typecheck_exprt::convert_binary_expr(binary_exprt expr)
Forall_operands(it, expr)
convert_expr(*it);

tc_binary_expr(expr);
typecheck_relation(expr);

return std::move(expr);
}
Expand All @@ -2622,7 +2715,7 @@ exprt verilog_typecheck_exprt::convert_binary_expr(binary_exprt expr)
Forall_operands(it, expr)
convert_expr(*it);

tc_binary_expr(expr);
typecheck_relation(expr);
no_bool_ops(expr);

return std::move(expr);
Expand Down
2 changes: 2 additions & 0 deletions src/verilog/verilog_typecheck_expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class verilog_typecheck_exprt:public verilog_typecheck_baset
{
}

static typet enum_decay(const typet &);
typet max_type(const typet &t1, const typet &t2);

// named blocks
Expand Down Expand Up @@ -132,6 +133,7 @@ class verilog_typecheck_exprt:public verilog_typecheck_baset
void implicit_typecast(exprt &, const typet &type);
void tc_binary_expr(exprt &);
void tc_binary_expr(const exprt &expr, exprt &op0, exprt &op1);
void typecheck_relation(binary_exprt &);
void no_bool_ops(exprt &);

// system functions
Expand Down