Skip to content

Commit

Permalink
Fix precedence for Verilog binary operators
Browse files Browse the repository at this point in the history
  • Loading branch information
nickg committed Aug 18, 2024
1 parent 929d4af commit 39a5721
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 35 deletions.
12 changes: 12 additions & 0 deletions lib/nvc/verilog-body.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,18 @@ package body verilog is
end if;
end function;

function "or" (l, r : t_packed_logic) return t_logic is
variable result : t_logic := '0';
begin
for i in l'range loop
result := result or l(i);
end loop;
for i in r'range loop
result := result or r(i);
end loop;
return result;
end function;

function "nor" (l, r : t_logic) return t_logic is
begin
return not (l or r);
Expand Down
1 change: 1 addition & 0 deletions lib/nvc/verilog.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ package verilog is
function "xor" (l, r : t_logic) return t_logic;

function "or" (l, r : t_logic) return t_logic;
function "or" (l, r : t_packed_logic) return t_logic;

function "not" (x : t_logic) return t_logic;
function "not" (x : t_packed_logic) return t_packed_logic;
Expand Down
1 change: 1 addition & 0 deletions src/lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ UNION ?i:union
<VLOG>"!=" { return tLOGNEQ; }
<VLOG>"===" { return tCASEEQ; }
<VLOG>"!==" { return tCASENEQ; }
<VLOG>"||" { return tLOGOR; }
<VLOG>"(*" { return tATTRBEGIN; }
<VLOG>"*)" { return tATTREND; }
<VLOG>"specify" { return tSPECIFY; }
Expand Down
2 changes: 1 addition & 1 deletion src/scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ const char *token_str(token_t tok)
"supply0", "supply1", "pulldown", "pullup", "===", "!==", "==", "!=",
"(*", "*)", "number", "forever", "[[", "]]", "specify", "endspecify",
"primitive", "endprimitive", "table", "endtable", "assign",
"level symbol", "edge symbol", "edge indicator", "buf",
"level symbol", "edge symbol", "edge indicator", "buf", "||",
};

if (tok >= 200 && tok - 200 < ARRAY_LEN(token_strs))
Expand Down
1 change: 1 addition & 0 deletions src/scan.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,5 +314,6 @@ bool is_scanned_as_psl(void);
#define tUDPEDGE 422
#define tUDPIND 423
#define tBUF 424
#define tLOGOR 425

#endif // _SCAN_H
25 changes: 23 additions & 2 deletions src/vlog/vlog-dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ static inline void tab(int indent)
print_syntax("%*s", indent, "");
}

static void vlog_dump_paren(vlog_node_t v, int indent)
{
switch (vlog_kind(v)) {
case V_REF:
case V_BIT_SELECT:
case V_NUMBER:
vlog_dump(v, indent);
break;
default:
print_syntax("(");
vlog_dump(v, indent);
print_syntax(")");
}
}

static void vlog_dump_module(vlog_node_t v, int indent)
{
print_syntax("#module %s", istr(vlog_ident2(v)));
Expand Down Expand Up @@ -329,14 +344,20 @@ static void vlog_dump_number(vlog_node_t v)

static void vlog_dump_binary(vlog_node_t v)
{
vlog_dump(vlog_left(v), 0);
vlog_dump_paren(vlog_left(v), 0);

switch (vlog_subkind(v)) {
case V_BINARY_OR: print_syntax(" | "); break;
case V_BINARY_AND: print_syntax(" & "); break;
case V_BINARY_CASE_EQ: print_syntax(" === "); break;
case V_BINARY_CASE_NEQ: print_syntax(" !== "); break;
case V_BINARY_LOG_EQ: print_syntax(" == "); break;
case V_BINARY_LOG_NEQ: print_syntax(" != "); break;
case V_BINARY_LOG_OR: print_syntax(" || "); break;
case V_BINARY_PLUS: print_syntax(" + "); break;
}

vlog_dump(vlog_right(v), 0);
vlog_dump_paren(vlog_right(v), 0);
}

static void vlog_dump_unary(vlog_node_t v)
Expand Down
2 changes: 2 additions & 0 deletions src/vlog/vlog-lower.c
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ static vcode_reg_t vlog_lower_binary(lower_unit_t *lu, vlog_binary_t op,
tb_cat(tb, "\"and\"(");
break;
case V_BINARY_OR:
case V_BINARY_LOG_OR:
tb_cat(tb, "\"or\"(");
break;
case V_BINARY_CASE_EQ:
Expand Down Expand Up @@ -417,6 +418,7 @@ static vcode_reg_t vlog_lower_binary(lower_unit_t *lu, vlog_binary_t op,
break;
case V_BINARY_LOG_EQ:
case V_BINARY_LOG_NEQ:
case V_BINARY_LOG_OR:
rtype = vlog_logic_type();
tb_cat(tb, T_LOGIC);
break;
Expand Down
1 change: 1 addition & 0 deletions src/vlog/vlog-node.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ typedef enum {
V_BINARY_LOG_NEQ,
V_BINARY_CASE_EQ,
V_BINARY_CASE_NEQ,
V_BINARY_LOG_OR,
} vlog_binary_t;

typedef enum {
Expand Down
111 changes: 87 additions & 24 deletions src/vlog/vlog-parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,21 @@ static vlog_node_t p_subroutine_call(vlog_kind_t kind)
return p_system_tf_call(kind);
}

static vlog_node_t p_mintypmax_expression(void)
{
// expression | expression : expression : expression

BEGIN("mintypmax expression");

consume(tLPAREN);

vlog_node_t inner = p_expression();

consume(tRPAREN);

return inner;
}

static vlog_node_t p_primary(void)
{
// primary_literal | empty_queue
Expand All @@ -935,8 +950,10 @@ static vlog_node_t p_primary(void)
return p_primary_literal();
case tSYSTASK:
return p_subroutine_call(V_SYSFUNC);
case tLPAREN:
return p_mintypmax_expression();
default:
one_of(tID, tSTRING, tNUMBER, tUNSIGNED, tSYSTASK);
one_of(tID, tSTRING, tNUMBER, tUNSIGNED, tSYSTASK, tLPAREN);
return p_select(error_marker());
}
}
Expand All @@ -949,13 +966,17 @@ static vlog_binary_t p_binary_operator(void)

BEGIN("binary operator");

switch (one_of(tBAR, tPLUS, tAMP, tCASEEQ, tCASENEQ)) {
case tBAR: return V_BINARY_OR;
case tAMP: return V_BINARY_AND;
case tCASEEQ: return V_BINARY_CASE_EQ;
switch (one_of(tBAR, tPLUS, tAMP, tCASEEQ, tCASENEQ, tLOGOR,
tLOGEQ, tLOGNEQ)) {
case tBAR: return V_BINARY_OR;
case tAMP: return V_BINARY_AND;
case tCASEEQ: return V_BINARY_CASE_EQ;
case tCASENEQ: return V_BINARY_CASE_NEQ;
case tLOGEQ: return V_BINARY_LOG_EQ;
case tLOGNEQ: return V_BINARY_LOG_NEQ;
case tLOGOR: return V_BINARY_LOG_OR;
case tPLUS:
default: return V_BINARY_PLUS;
default: return V_BINARY_PLUS;
}
}
static vlog_unary_t p_unary_operator(void)
Expand All @@ -973,48 +994,90 @@ static vlog_unary_t p_unary_operator(void)
}
}

static vlog_node_t p_expression(void)
static vlog_node_t p_nonbinary_expression(void)
{
// primary | unary_operator { attribute_instance } primary
// | inc_or_dec_expression | ( operator_assignment )
// | expression binary_operator { attribute_instance } expression
// | conditional_expression | inside_expression | tagged_union_expression

BEGIN("expression");

vlog_node_t head;
switch (peek()) {
case tID:
case tSTRING:
case tNUMBER:
case tUNSIGNED:
case tSYSTASK:
head = p_primary();
break;
case tLPAREN:
return p_primary();
case tMINUS:
case tTILDE:
case tBANG:
head = vlog_new(V_UNARY);
vlog_set_subkind(head, p_unary_operator());
vlog_set_value(head, p_primary());
vlog_set_loc(head, CURRENT_LOC);
break;
{
vlog_node_t v = vlog_new(V_UNARY);
vlog_set_subkind(v, p_unary_operator());
vlog_set_value(v, p_primary());
vlog_set_loc(v, CURRENT_LOC);
return v;
}
default:
one_of(tID, tSTRING, tNUMBER, tUNSIGNED, tMINUS, tTILDE, tBANG, tSYSTASK);
return p_select(error_marker());
}
}

static bool peek_binary_operator(int *prec)
{
// See LRM 1800-2017 section 11.3.2 for operator precedence table

if (scan(tBAR, tPLUS, tAMP, tCASEEQ, tCASENEQ)) {
switch (peek()) {
case tPLUS: *prec = 10; return true;
case tCASEEQ:
case tCASENEQ:
case tLOGEQ:
case tLOGNEQ: *prec = 7; return true;
case tAMP: *prec = 6; return true;
case tBAR: *prec = 4; return true;
case tLOGOR: *prec = 2; return true;
default:
return false;
}
}

static vlog_node_t p_binary_expression(vlog_node_t lhs, int min_prec)
{
// Precedence climbing method, see
// https://en.wikipedia.org/wiki/Operator-precedence_parser

int prec1;
while (peek_binary_operator(&prec1) && prec1 >= min_prec) {
vlog_node_t v = vlog_new(V_BINARY);
vlog_set_subkind(v, p_binary_operator());
vlog_set_left(v, head);
vlog_set_right(v, p_expression());
vlog_set_left(v, lhs);

vlog_node_t rhs = p_nonbinary_expression();

int prec2;
while (peek_binary_operator(&prec2) && prec2 > prec1)
rhs = p_binary_expression(rhs, prec1 + (prec2 > prec1));

vlog_set_right(v, rhs);
vlog_set_loc(v, CURRENT_LOC);
return v;
lhs = v;
}
else
return head;

return lhs;
}

static vlog_node_t p_expression(void)
{
// primary | unary_operator { attribute_instance } primary
// | inc_or_dec_expression | ( operator_assignment )
// | expression binary_operator { attribute_instance } expression
// | conditional_expression | inside_expression | tagged_union_expression

BEGIN("expression");

vlog_node_t head = p_nonbinary_expression();
return p_binary_expression(head, 0);
}

static vlog_node_t p_event_expression(void)
Expand Down
11 changes: 11 additions & 0 deletions test/dump/vlog1.v
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,14 @@ primitive multiplexer (mux, control, dataA, dataB);
1 ? 0 : 0 ;
endtable
endprimitive

module mod3; // Check operator precedence
wire x, y, z;
initial begin
if (x || y === z);
if (x & y | y & z === x + z);
if (x == y || y == z);
if ((x & y) == z);
end
endmodule // mod3

12 changes: 4 additions & 8 deletions test/regress/vlog11.v
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,22 @@ module vlog11;
i1 = 1;
i2 = 0;
#1 $display("%d %d", o1, o2);
if (o1 !== 1) $display("FAILED");
if (o2 !== 1) $display("FAILED");
if (o1 !== 1 || o2 !== 1) $display("FAILED");

i1 = 0;
i2 = 1;
#1 $display("%d %d", o1, o2);
if (o1 !== 1) $display("FAILED");
if (o2 !== 1) $display("FAILED");
if (o1 !== 1 || o2 !== 1) $display("FAILED");

i1 = 1;
i2 = 1;
#1 $display("%d %d", o1, o2);
if (o1 !== 0) $display("FAILED");
if (o2 !== 0) $display("FAILED");
if (o1 !== 0 || o2 !== 0) $display("FAILED");

i1 = 0;
i2 = 0;
#1 $display("%d %d", o1, o2);
if (o1 !== 1) $display("FAILED");
if (o2 !== 0) $display("FAILED");
if (o1 !== 1 || o2 !== 0) $display("FAILED");

//i1 = 1;
//i2 = 1'bx;
Expand Down
18 changes: 18 additions & 0 deletions test/test_dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,24 @@ START_TEST(test_vlog1)
"endprimitive // multiplexer\n\n");
tb_rewind(tb);

vlog_node_t m4 = vlog_parse();
fail_if(m4 == NULL);

vlog_dump(m4, 0);
diff_dump(tb_get(tb),
"module mod3;\n"
" wire x;\n"
" wire y;\n"
" wire z;\n"
" initial begin\n"
" if (x || (y === z));\n"
" if ((x & y) | (y & (z === (x + z))));\n"
" if ((x == y) || (y == z));\n"
" if ((x & y) == z);\n"
" end\n"
"endmodule // mod3\n\n");
tb_rewind(tb);

fail_if_errors();
}
END_TEST
Expand Down

0 comments on commit 39a5721

Please sign in to comment.