diff --git a/lib/nvc/verilog-body.vhd b/lib/nvc/verilog-body.vhd index ce30607c6..a850b24c7 100644 --- a/lib/nvc/verilog-body.vhd +++ b/lib/nvc/verilog-body.vhd @@ -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); diff --git a/lib/nvc/verilog.vhd b/lib/nvc/verilog.vhd index 08374f4c0..ef862407b 100644 --- a/lib/nvc/verilog.vhd +++ b/lib/nvc/verilog.vhd @@ -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; diff --git a/src/lexer.l b/src/lexer.l index 4e427a23a..831c8bb4b 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -563,6 +563,7 @@ UNION ?i:union "!=" { return tLOGNEQ; } "===" { return tCASEEQ; } "!==" { return tCASENEQ; } +"||" { return tLOGOR; } "(*" { return tATTRBEGIN; } "*)" { return tATTREND; } "specify" { return tSPECIFY; } diff --git a/src/scan.c b/src/scan.c index 88910a730..686db02e3 100644 --- a/src/scan.c +++ b/src/scan.c @@ -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)) diff --git a/src/scan.h b/src/scan.h index 9cad98285..b8579cd3b 100644 --- a/src/scan.h +++ b/src/scan.h @@ -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 diff --git a/src/vlog/vlog-dump.c b/src/vlog/vlog-dump.c index d89e61310..5e4a2e203 100644 --- a/src/vlog/vlog-dump.c +++ b/src/vlog/vlog-dump.c @@ -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))); @@ -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) diff --git a/src/vlog/vlog-lower.c b/src/vlog/vlog-lower.c index 5aaa17ae0..819a8d1b1 100644 --- a/src/vlog/vlog-lower.c +++ b/src/vlog/vlog-lower.c @@ -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: @@ -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; diff --git a/src/vlog/vlog-node.h b/src/vlog/vlog-node.h index f14ce6e87..46bd1db59 100644 --- a/src/vlog/vlog-node.h +++ b/src/vlog/vlog-node.h @@ -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 { diff --git a/src/vlog/vlog-parse.c b/src/vlog/vlog-parse.c index 04ef30081..46685f0f0 100644 --- a/src/vlog/vlog-parse.c +++ b/src/vlog/vlog-parse.c @@ -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 @@ -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()); } } @@ -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) @@ -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) diff --git a/test/dump/vlog1.v b/test/dump/vlog1.v index fbec94202..460e7b8c1 100644 --- a/test/dump/vlog1.v +++ b/test/dump/vlog1.v @@ -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 + diff --git a/test/regress/vlog11.v b/test/regress/vlog11.v index c139b5626..b7d1ba4c5 100644 --- a/test/regress/vlog11.v +++ b/test/regress/vlog11.v @@ -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; diff --git a/test/test_dump.c b/test/test_dump.c index dfddd3369..dd6d07c74 100644 --- a/test/test_dump.c +++ b/test/test_dump.c @@ -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