From 87015b6fe66f50a7047bb807ebe0ee7da3244074 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Fri, 22 Sep 2023 11:37:22 +0100 Subject: [PATCH] Lowering for Verilog if statements --- lib/nvc/verilog-body.vhd | 2 +- src/lexer.l | 1 + src/vlog/vlog-dump.c | 31 ++++++++++++++++----- src/vlog/vlog-lower.c | 60 ++++++++++++++++++++++++++++++++++++++++ src/vlog/vlog-node.c | 5 ++++ src/vlog/vlog-node.h | 1 + src/vlog/vlog-parse.y | 24 +++++++++++++++- src/vlog/vlog-sem.c | 20 ++++++++++++++ test/regress/vlog3.v | 8 +++--- test/test_vlog.c | 4 +++ test/vlog/parse1.v | 3 ++ 11 files changed, 146 insertions(+), 13 deletions(-) diff --git a/lib/nvc/verilog-body.vhd b/lib/nvc/verilog-body.vhd index 56adda34..c1ff4ff2 100644 --- a/lib/nvc/verilog-body.vhd +++ b/lib/nvc/verilog-body.vhd @@ -26,7 +26,7 @@ package body verilog is variable r : delay_length := 0 fs; variable add : delay_length := 1 fs; begin - for i in v'reverse_range loop + for i in v'range loop if v(i) = '1' then r := r + add; end if; diff --git a/src/lexer.l b/src/lexer.l index 81eacd8b..06d55020 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -528,6 +528,7 @@ UNION ?i:union "wire" { return tWIRE; } "assign" { return tASSIGN; } "if" { return tIF; } +"else" { return tELSE; } {SYSTASK} { yylval.str = xstrdup(yytext); return tSYSTASK; } {VLOG_ID} { yylval.str = xstrdup(yytext); return tID; } diff --git a/src/vlog/vlog-dump.c b/src/vlog/vlog-dump.c index fba72c52..4131a5e7 100644 --- a/src/vlog/vlog-dump.c +++ b/src/vlog/vlog-dump.c @@ -200,14 +200,31 @@ static void vlog_dump_assign(vlog_node_t v, int indent) static void vlog_dump_if(vlog_node_t v, int indent) { tab(indent); - print_syntax("#if ("); - vlog_node_t c0 = vlog_cond(v, 0); - vlog_dump(vlog_value(c0), 0); - print_syntax(")\n"); + print_syntax("#if "); - const int nstmts = vlog_stmts(c0); - for (int i = 0; i < nstmts; i++) - vlog_dump(vlog_stmt(c0, i), indent + 2); + const int nconds = vlog_conds(v); + for (int i = 0; i < nconds; i++) { + if (i > 0) { + tab(indent); + print_syntax("#else "); + } + + vlog_node_t c = vlog_cond(v, i); + if (vlog_has_value(c)) { + print_syntax("("); + vlog_dump(vlog_value(c), 0); + print_syntax(")"); + } + + const int nstmts = vlog_stmts(c); + if (nstmts == 0) + print_syntax(";\n"); + else { + print_syntax("\n"); + for (int i = 0; i < nstmts; i++) + vlog_dump(vlog_stmt(c, i), indent + 2); + } + } } static void vlog_dump_systask(vlog_node_t v, int indent) diff --git a/src/vlog/vlog-lower.c b/src/vlog/vlog-lower.c index d2e8f6fc..e842167a 100644 --- a/src/vlog/vlog-lower.c +++ b/src/vlog/vlog-lower.c @@ -99,6 +99,24 @@ static vcode_reg_t vlog_lower_to_time(lower_unit_t *lu, vcode_reg_t reg) return emit_fcall(func, vtime, vtime, VCODE_CC_VHDL, args, ARRAY_LEN(args)); } +static vcode_reg_t vlog_lower_to_bool(lower_unit_t *lu, vcode_reg_t reg) +{ + + switch (vcode_reg_kind(reg)) { + case VCODE_TYPE_INT: + { + vcode_type_t vlogic = vlog_logic_type(); + vcode_reg_t one_reg = emit_const(vlogic, 1); + assert(vtype_eq(vcode_reg_type(reg), vlogic)); + return emit_cmp(VCODE_CMP_EQ, reg, one_reg); + } + break; + default: + vcode_dump(); + fatal_trace("cannot convert r%d to bool", reg); + } +} + static void vlog_lower_signal_decl(lower_unit_t *lu, vlog_node_t port) { vcode_type_t vlogic = vlog_logic_type(); @@ -375,6 +393,45 @@ static void vlog_lower_systask(lower_unit_t *lu, vlog_node_t v) } } +static void vlog_lower_if(lower_unit_t *lu, vlog_node_t v) +{ + vcode_block_t true_bb = emit_block(); + vcode_block_t false_bb = emit_block(), skip_bb = false_bb; + + const int nconds = vlog_conds(v); + assert(nconds == 1 || nconds == 2); + + if (nconds == 2) + skip_bb = emit_block(); + + vlog_node_t c0 = vlog_cond(v, 0); + + vcode_reg_t test_reg = vlog_lower_rvalue(lu, vlog_value(c0)); + vcode_reg_t bool_reg = vlog_lower_to_bool(lu, test_reg); + emit_cond(bool_reg, true_bb, false_bb); + + vcode_select_block(true_bb); + + vlog_lower_stmts(lu, c0); + + if (!vcode_block_finished()) + emit_jump(skip_bb); + + if (nconds == 2) { + vlog_node_t c1 = vlog_cond(v, 1); + assert(!vlog_has_value(c1)); + + vcode_select_block(false_bb); + + vlog_lower_stmts(lu, c1); + + if (!vcode_block_finished()) + emit_jump(skip_bb); + } + + vcode_select_block(skip_bb); +} + static void vlog_lower_stmts(lower_unit_t *lu, vlog_node_t v) { const int nstmts = vlog_stmts(v); @@ -395,6 +452,9 @@ static void vlog_lower_stmts(lower_unit_t *lu, vlog_node_t v) case V_SYSTASK: vlog_lower_systask(lu, s); break; + case V_IF: + vlog_lower_if(lu, s); + break; default: CANNOT_HANDLE(s); } diff --git a/src/vlog/vlog-node.c b/src/vlog/vlog-node.c index 276c7f93..0eedbf5f 100644 --- a/src/vlog/vlog-node.c +++ b/src/vlog/vlog-node.c @@ -330,6 +330,11 @@ vlog_node_t vlog_value(vlog_node_t v) return container_of(item->object, struct _vlog_node, object); } +bool vlog_has_value(vlog_node_t v) +{ + return lookup_item(&vlog_object, v, I_VALUE)->object != NULL; +} + void vlog_set_value(vlog_node_t v, vlog_node_t e) { lookup_item(&vlog_object, v, I_VALUE)->object = &(e->object); diff --git a/src/vlog/vlog-node.h b/src/vlog/vlog-node.h index a9f4b850..ed3ed1a4 100644 --- a/src/vlog/vlog-node.h +++ b/src/vlog/vlog-node.h @@ -126,6 +126,7 @@ unsigned vlog_subkind(vlog_node_t v); void vlog_set_subkind(vlog_node_t v, unsigned sub); vlog_node_t vlog_value(vlog_node_t v); +bool vlog_has_value(vlog_node_t v); void vlog_set_value(vlog_node_t v, vlog_node_t e); vlog_node_t vlog_target(vlog_node_t v); diff --git a/src/vlog/vlog-parse.y b/src/vlog/vlog-parse.y index d66e6a03..33656fa2 100644 --- a/src/vlog/vlog-parse.y +++ b/src/vlog/vlog-parse.y @@ -146,6 +146,7 @@ static bool is_decl(vlog_node_t v) %token tWIRE 358 "wire" %token tASSIGN 227 "assign" %token tIF 234 "if" +%token tELSE 255 "else" %token tEOF 0 "end of file" /* @@ -158,6 +159,9 @@ static bool is_decl(vlog_node_t v) %nonassoc tABS tNOT tNEW */ +%precedence "then" +%precedence tELSE + %define parse.error verbose %expect 0 @@ -449,7 +453,7 @@ list_of_statements: ; conditional_statement: - tIF '(' expression ')' statement_or_null + tIF '(' expression ')' statement_or_null %prec "then" { vlog_node_t c = vlog_new(V_COND); vlog_set_loc(c, &@3); @@ -461,6 +465,24 @@ conditional_statement: vlog_set_loc($$, &@$); vlog_add_cond($$, c); } + | tIF '(' expression ')' statement_or_null tELSE statement_or_null + { + vlog_node_t c1 = vlog_new(V_COND); + vlog_set_loc(c1, &@3); + vlog_set_value(c1, $3); + if ($5 != NULL) + vlog_add_stmt(c1, $5); + + vlog_node_t c2 = vlog_new(V_COND); + vlog_set_loc(c2, &@6); + if ($7 != NULL) + vlog_add_stmt(c2, $7); + + $$ = vlog_new(V_IF); + vlog_set_loc($$, &@$); + vlog_add_cond($$, c1); + vlog_add_cond($$, c2); + } ; system_task_enable: diff --git a/src/vlog/vlog-sem.c b/src/vlog/vlog-sem.c index 0bb659ae..27b8c30b 100644 --- a/src/vlog/vlog-sem.c +++ b/src/vlog/vlog-sem.c @@ -222,6 +222,23 @@ static void vlog_check_systask(vlog_node_t call) vlog_check(vlog_param(call, i)); } +static void vlog_check_if(vlog_node_t stmt) +{ + const int nconds = vlog_conds(stmt); + for (int i = 0; i < nconds; i++) { + vlog_node_t c = vlog_cond(stmt, i); + + if (vlog_has_value(c)) + vlog_check(vlog_value(c)); + else + assert(i == nconds - 1); + + const int nstmts = vlog_stmts(c); + for (int i = 0; i < nstmts; i++) + vlog_check(vlog_stmt(c, i)); + } +} + static void vlog_check_port_decl(vlog_node_t port) { vlog_insert_decl(port); @@ -308,6 +325,9 @@ void vlog_check(vlog_node_t v) case V_STRING: vlog_check_string(v); break; + case V_IF: + vlog_check_if(v); + break; default: fatal_trace("cannot check verilog node %s", vlog_kind_str(vlog_kind(v))); } diff --git a/test/regress/vlog3.v b/test/regress/vlog3.v index bf109729..becafcd9 100644 --- a/test/regress/vlog3.v +++ b/test/regress/vlog3.v @@ -4,16 +4,16 @@ module vlog3; initial begin x <= 1; $display("x ==> %d", x); - /*if (x) + if (x) $display("x is true"); else - $display("x is false");*/ + $display("x is false"); #1; $display("x ==> %d", x); - /*if (x) + if (x) $display("x is true"); else - $display("x is false");*/ + $display("x is false"); #1 x <= 0; $display("x ==> %d", x); #1 x <= 4; diff --git a/test/test_vlog.c b/test/test_vlog.c index d0d81317..3cb38c4d 100644 --- a/test/test_vlog.c +++ b/test/test_vlog.c @@ -197,6 +197,10 @@ START_TEST(test_parse1) fail_unless(vlog_kind(s1s0s3) == V_IF); fail_unless(vlog_conds(s1s0s3) == 1); + vlog_node_t s1s0s4 = vlog_stmt(s1s0, 4); + fail_unless(vlog_kind(s1s0s4) == V_IF); + fail_unless(vlog_conds(s1s0s4) == 2); + fail_unless(vlog_parse() == NULL); fail_if_errors(); diff --git a/test/vlog/parse1.v b/test/vlog/parse1.v index 7b6a2d11..f5f5a419 100644 --- a/test/vlog/parse1.v +++ b/test/vlog/parse1.v @@ -7,5 +7,8 @@ module parse1; $finish; if (x); if (x) z <= 1; + if (x) $display("yes"); + else if (z); + else $display("no"); end endmodule // parse1 -- 2.39.2