From 69e521071e76613097bd349dcf4a15dfa5593055 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Thu, 19 Dec 2024 19:28:48 +0000 Subject: [PATCH] Parse Verilog concatenation --- src/vlog/vlog-dump.c | 16 +++++++++++++ src/vlog/vlog-node.c | 5 +++- src/vlog/vlog-node.h | 1 + src/vlog/vlog-parse.c | 53 ++++++++++++++++++++++++++++++++++++------ src/vlog/vlog-pp.l | 2 ++ src/vlog/vlog-sem.c | 54 +++++++++++++++++++++++++++++++++++++------ test/dump/vlog1.v | 1 + test/run_ivtest.rb | 2 +- test/test_dump.c | 1 + test/test_vlog.c | 25 +++++++++++++++++++- test/vlog/concat1.v | 12 ++++++++++ test/vlog/parse1.v | 2 ++ 12 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 test/vlog/concat1.v diff --git a/src/vlog/vlog-dump.c b/src/vlog/vlog-dump.c index c808cb51d..6e0391c95 100644 --- a/src/vlog/vlog-dump.c +++ b/src/vlog/vlog-dump.c @@ -521,6 +521,19 @@ static void vlog_dump_cond_expr(vlog_node_t v, int indent) vlog_dump(vlog_right(v), 0); } +static void vlog_dump_concat(vlog_node_t v, int indent) +{ + print_syntax("{"); + + const int nparams = vlog_params(v); + for (int i = 0; i < nparams; i++) { + if (i > 0) print_syntax(", "); + vlog_dump(vlog_param(v, i), 0); + } + + print_syntax("}"); +} + void vlog_dump(vlog_node_t v, int indent) { switch (vlog_kind(v)) { @@ -631,6 +644,9 @@ void vlog_dump(vlog_node_t v, int indent) case V_COND_EXPR: vlog_dump_cond_expr(v, indent); break; + case V_CONCAT: + vlog_dump_concat(v, indent); + break; default: print_syntax("\n"); fflush(stdout); diff --git a/src/vlog/vlog-node.c b/src/vlog/vlog-node.c index 040e23ee4..7650c1864 100644 --- a/src/vlog/vlog-node.c +++ b/src/vlog/vlog-node.c @@ -171,6 +171,9 @@ static const imask_t has_map[V_LAST_NODE_KIND] = { // V_REAL (I_DVAL), + + // V_CONCAT + (I_PARAMS), }; static const char *kind_text_map[V_LAST_NODE_KIND] = { @@ -186,7 +189,7 @@ static const char *kind_text_map[V_LAST_NODE_KIND] = { "V_UNION_DECL", "V_STRUCT_DECL", "V_EVENT_CONTROL", "V_EMPTY", "V_REPEAT", "V_WHILE", "V_DO_WHILE", "V_TASK_DECL", "V_FUNC_DECL", "V_WAIT", "V_PARAM_DECL", "V_COND_EXPR", - "V_REAL", + "V_REAL", "V_CONCAT", }; static const change_allowed_t change_allowed[] = { diff --git a/src/vlog/vlog-node.h b/src/vlog/vlog-node.h index d532d30f5..f65d1a066 100644 --- a/src/vlog/vlog-node.h +++ b/src/vlog/vlog-node.h @@ -93,6 +93,7 @@ typedef enum { V_PARAM_DECL, V_COND_EXPR, V_REAL, + V_CONCAT, V_LAST_NODE_KIND } vlog_kind_t; diff --git a/src/vlog/vlog-parse.c b/src/vlog/vlog-parse.c index a94e7ad21..da33e4d0d 100644 --- a/src/vlog/vlog-parse.c +++ b/src/vlog/vlog-parse.c @@ -1156,6 +1156,26 @@ static vlog_node_t p_mintypmax_expression(void) return p_expression(); } +static vlog_node_t p_concatenation(void) +{ + // { expression { , expression } } + + BEGIN("concatenation"); + + consume(tLBRACE); + + vlog_node_t v = vlog_new(V_CONCAT); + + do { + vlog_add_param(v, p_expression()); + } while (optional(tCOMMA)); + + consume(tRBRACE); + + vlog_set_loc(v, CURRENT_LOC); + return v; +} + static vlog_node_t p_primary(void) { // primary_literal | empty_queue @@ -1186,8 +1206,11 @@ static vlog_node_t p_primary(void) consume(tRPAREN); return expr; } + case tLBRACE: + return p_concatenation(); default: - one_of(tID, tSTRING, tNUMBER, tUNSIGNED, tREAL, tSYSTASK, tLPAREN); + one_of(tID, tSTRING, tNUMBER, tUNSIGNED, tREAL, tSYSTASK, tLPAREN, + tLBRACE); return p_select(error_marker()); } } @@ -1255,9 +1278,10 @@ static vlog_node_t p_nonbinary_expression(void) case tSTRING: case tNUMBER: case tUNSIGNED: + case tREAL: case tSYSTASK: case tLPAREN: - case tREAL: + case tLBRACE: return p_primary(); case tMINUS: case tTILDE: @@ -1270,7 +1294,8 @@ static vlog_node_t p_nonbinary_expression(void) return v; } default: - one_of(tID, tSTRING, tNUMBER, tUNSIGNED, tMINUS, tTILDE, tBANG, tSYSTASK); + one_of(tID, tSTRING, tNUMBER, tUNSIGNED, tREAL, tSYSTASK, tLPAREN, + tLBRACE, tMINUS, tTILDE, tBANG); return p_select(error_marker()); } } @@ -1493,11 +1518,25 @@ static vlog_node_t p_net_lvalue(void) BEGIN("net lvalue"); - ident_t id = p_identifier(); - vlog_node_t v = p_constant_select(id); + if (optional(tLBRACE)) { + vlog_node_t v = vlog_new(V_CONCAT); - vlog_set_loc(v, CURRENT_LOC); - return v; + do { + vlog_add_param(v, p_net_lvalue()); + } while (optional(tCOMMA)); + + consume(tRBRACE); + + vlog_set_loc(v, CURRENT_LOC); + return v; + } + else { + ident_t id = p_identifier(); + vlog_node_t v = p_constant_select(id); + + vlog_set_loc(v, CURRENT_LOC); + return v; + } } static vlog_node_t p_operator_assignment(vlog_node_t lhs) diff --git a/src/vlog/vlog-pp.l b/src/vlog/vlog-pp.l index 2a4bcbb20..6baf1d480 100644 --- a/src/vlog/vlog-pp.l +++ b/src/vlog/vlog-pp.l @@ -103,6 +103,8 @@ ID [a-zA-Z_]([a-zA-Z0-9_$])* `timescale | `resetall | `pragma | +`begin_keywords | +`end_keywords | `celldefine | `endcelldefine { tb_cat(output, vlogpp_text); } diff --git a/src/vlog/vlog-sem.c b/src/vlog/vlog-sem.c index c66da1a58..28513c645 100644 --- a/src/vlog/vlog-sem.c +++ b/src/vlog/vlog-sem.c @@ -244,6 +244,40 @@ static void vlog_check_bassign(vlog_node_t stmt) vlog_check_variable_target(target); } +static void vlog_check_net_lvalue(vlog_node_t v, vlog_node_t where) +{ + switch (vlog_kind(v)) { + case V_NET_DECL: + break; + case V_PORT_DECL: + if (vlog_has_ref(v)) + vlog_check_net_lvalue(vlog_ref(v), where); + break; + case V_BIT_SELECT: + case V_REF: + if (vlog_has_ref(v)) + vlog_check_net_lvalue(vlog_ref(v), v); + break; + case V_CONCAT: + { + const int nparams = vlog_params(v); + for (int i = 0; i < nparams; i++) { + vlog_node_t p = vlog_param(v, i); + vlog_check_net_lvalue(p, p); + } + } + break; + default: + { + diag_t *d = diag_new(DIAG_ERROR, vlog_loc(where)); + name_for_diag(d, where, "target"); + diag_suppress(d, has_error(where)); + diag_printf(d, " cannot be driven by continuous assignment"); + diag_emit(d); + } + } +} + static void vlog_check_assign(vlog_node_t stmt) { vlog_node_t target = vlog_target(stmt); @@ -252,13 +286,7 @@ static void vlog_check_assign(vlog_node_t stmt) vlog_node_t value = vlog_value(stmt); vlog_check(value); - if (!vlog_is_net(target)) { - diag_t *d = diag_new(DIAG_ERROR, vlog_loc(target)); - name_for_diag(d, target, "target"); - diag_suppress(d, has_error(target)); - diag_printf(d, " cannot be driven by continuous assignment"); - diag_emit(d); - } + vlog_check_net_lvalue(target, target); } static void vlog_check_timing(vlog_node_t timing) @@ -613,6 +641,15 @@ static void vlog_check_cond_expr(vlog_node_t expr) vlog_check(vlog_right(expr)); } +static void vlog_check_concat(vlog_node_t expr) +{ + const int nparams = vlog_params(expr); + for (int i = 0; i < nparams; i++) { + vlog_node_t p = vlog_param(expr, i); + vlog_check(p); + } +} + void vlog_check(vlog_node_t v) { switch (vlog_kind(v)) { @@ -729,6 +766,9 @@ void vlog_check(vlog_node_t v) case V_COND_EXPR: vlog_check_cond_expr(v); break; + case V_CONCAT: + vlog_check_concat(v); + break; default: fatal_at(vlog_loc(v), "cannot check verilog node %s", vlog_kind_str(vlog_kind(v))); diff --git a/test/dump/vlog1.v b/test/dump/vlog1.v index 3bc1d62e0..ccb62f210 100644 --- a/test/dump/vlog1.v +++ b/test/dump/vlog1.v @@ -50,5 +50,6 @@ module mod3; // Check operator precedence end real v1 = 1.5; integer v2 = x ? 1 : 2; + assign z = {x, y}; endmodule // mod3 diff --git a/test/run_ivtest.rb b/test/run_ivtest.rb index e4119db27..e484c79b6 100755 --- a/test/run_ivtest.rb +++ b/test/run_ivtest.rb @@ -18,7 +18,7 @@ IvtestDir = Pathname.new(ARGV[0]).realpath GitRev = IO::popen("git rev-parse --short HEAD").read.chomp Tool = ENV['NVC'] || 'nvc' -ExpectFails = 1324 +ExpectFails = 1280 ENV['NVC_COLORS'] = 'always' diff --git a/test/test_dump.c b/test/test_dump.c index ee758f0f2..24fa3138f 100644 --- a/test/test_dump.c +++ b/test/test_dump.c @@ -564,6 +564,7 @@ START_TEST(test_vlog1) " if ((x == y) || (y == z));\n" " if ((x & y) == z);\n" " end\n" + " assign z = {x, y};\n" "endmodule // mod3\n\n"); tb_rewind(tb); diff --git a/test/test_vlog.c b/test/test_vlog.c index 3abeec52c..fc0ed24c9 100644 --- a/test/test_vlog.c +++ b/test/test_vlog.c @@ -169,7 +169,7 @@ START_TEST(test_parse1) vlog_node_t m = vlog_parse(); fail_if(m == NULL); fail_unless(vlog_kind(m) == V_MODULE); - fail_unless(vlog_stmts(m) == 12); + fail_unless(vlog_stmts(m) == 14); fail_unless(vlog_ports(m) == 0); fail_unless(vlog_decls(m) == 14); @@ -632,6 +632,28 @@ START_TEST(test_pp3) } END_TEST +START_TEST(test_concat1) +{ + input_from_file(TESTDIR "/vlog/concat1.v"); + + const error_t expect[] = { + { 10, "'q' cannot be driven by continuous assignment" }, + { -1, NULL } + }; + expect_errors(expect); + + vlog_node_t m = vlog_parse(); + fail_if(m == NULL); + fail_unless(vlog_kind(m) == V_MODULE); + + vlog_check(m); + + fail_unless(vlog_parse() == NULL); + + check_expected_errors(); +} +END_TEST + Suite *get_vlog_tests(void) { Suite *s = suite_create("vlog"); @@ -656,6 +678,7 @@ Suite *get_vlog_tests(void) tcase_add_test(tc, test_union1); tcase_add_test(tc, test_param1); tcase_add_test(tc, test_pp3); + tcase_add_test(tc, test_concat1); suite_add_tcase(s, tc); return s; diff --git a/test/vlog/concat1.v b/test/vlog/concat1.v new file mode 100644 index 000000000..0036895cd --- /dev/null +++ b/test/vlog/concat1.v @@ -0,0 +1,12 @@ +module concat1; + wire x, y; + wire [7:0] z; + real r1; + reg q; + + assign a1 = {x, y}; // OK + assign a2 = {r1, x}; // Error + assign {z[0], z[4]} = {x, y}; // OK + assign {x, q} = 2'b11; // Error + +endmodule // concat1 diff --git a/test/vlog/parse1.v b/test/vlog/parse1.v index 3bfed8d18..d02155445 100644 --- a/test/vlog/parse1.v +++ b/test/vlog/parse1.v @@ -54,4 +54,6 @@ module parse1; real r3 = 1.0; shortreal r4 = 6.7; realtime r5 = 1.0; + assign r2 = {x, y}; + assign {r1, r2} = {x, y}; endmodule // parse1