From c7b8eea3657eea5c6bb107fc37ea044e0bbb06a1 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sun, 25 Feb 2024 19:12:36 +0000 Subject: [PATCH] Add some basic support for Verilog module instantiation --- src/dump.c | 24 +++++++-- src/elab.c | 98 +++++++++++++++++++++++++++++++--- src/vlog/vlog-dump.c | 24 +++++++++ src/vlog/vlog-lower.c | 2 + src/vlog/vlog-node.c | 4 ++ src/vlog/vlog-node.h | 1 + src/vlog/vlog-parse.y | 41 +++++++++++++- src/vlog/vlog-sem.c | 12 +++++ test/dump/vlog1.v | 1 + test/elab/vlog1.v | 13 +++++ test/regress/gold/issue808.txt | 2 +- test/regress/issue808.v | 10 +++- test/test_dump.c | 1 + test/test_elab.c | 31 +++++++++++ 14 files changed, 251 insertions(+), 13 deletions(-) create mode 100644 test/elab/vlog1.v diff --git a/src/dump.c b/src/dump.c index fddb08df..ba87df85 100644 --- a/src/dump.c +++ b/src/dump.c @@ -23,6 +23,7 @@ #include "psl/psl-phase.h" #include "tree.h" #include "type.h" +#include "vlog/vlog-node.h" #include "vlog/vlog-phase.h" #include @@ -589,15 +590,32 @@ static void dump_binding(tree_t t, int indent) static void dump_stmts(tree_t t, int indent) { + bool last_was_newline = false; const int nstmts = tree_stmts(t); for (int i = 0; i < nstmts; i++) { tree_t s = tree_stmt(t, i); const tree_kind_t kind = tree_kind(s); - const bool needs_newline = - kind == T_BLOCK || kind == T_PROCESS || kind == T_INSTANCE; - if (needs_newline && i > 0) + + bool needs_newline; + if (kind == T_VERILOG) { + const vlog_kind_t vkind = vlog_kind(tree_vlog(s)); + needs_newline = (vkind == V_ALWAYS || vkind == V_INITIAL); + } + else + needs_newline = (kind == T_BLOCK || kind == T_PROCESS + || kind == T_INSTANCE); + + if (needs_newline && i > 0 && !last_was_newline) print_syntax("\n"); + dump_stmt(s, indent); + + if (needs_newline && i + 1 < nstmts) { + print_syntax("\n"); + last_was_newline = true; + } + else + last_was_newline = false; } } diff --git a/src/elab.c b/src/elab.c index 55629d22..dfa0ac71 100644 --- a/src/elab.c +++ b/src/elab.c @@ -445,8 +445,7 @@ static tree_t elab_to_verilog(type_t from, type_t to) return NULL; } -static tree_t elab_mixed_binding(tree_t comp, mod_cache_t *mc, - const elab_ctx_t *ctx) +static tree_t elab_mixed_binding(tree_t comp, mod_cache_t *mc) { assert(tree_kind(comp) == T_COMPONENT); @@ -491,9 +490,6 @@ static tree_t elab_mixed_binding(tree_t comp, mod_cache_t *mc, } if (name != tree_ident(cport)) { - tree_t comp = tree_ref(ctx->inst); - assert(tree_kind(comp) == T_COMPONENT); - error_at(tree_loc(cport), "expected VHDL port name %s to match " "Verilog port name %s in component %s", istr(tree_ident(cport)), istr(vlog_ident(mport)), @@ -561,6 +557,39 @@ static tree_t elab_mixed_binding(tree_t comp, mod_cache_t *mc, return bind; } +static tree_t elab_verilog_binding(vlog_node_t inst, mod_cache_t *mc, + const elab_ctx_t *ctx) +{ + assert(vlog_kind(inst) == V_MOD_INST); + + tree_t bind = tree_new(T_BINDING); + tree_set_ident(bind, vlog_ident(mc->module)); + tree_set_loc(bind, vlog_loc(inst)); + tree_set_ref(bind, mc->wrap); + tree_set_class(bind, C_ENTITY); + + const int nports = vlog_ports(mc->module); + const int nparams = vlog_params(inst); + + if (nports != nparams) { + error_at(vlog_loc(inst), "expected %d port connections for module %s " + "but found %d", nports, istr(vlog_ident(mc->module)), nparams); + return NULL; + } + + for (int i = 0; i < nports; i++) { + vlog_node_t conn = vlog_param(inst, i); + assert(vlog_kind(conn) == V_REF); + + tree_t decl = search_decls(ctx->out, vlog_ident(conn), 0); + assert(decl != NULL); + + add_param(bind, make_ref(decl), P_POS, NULL); + } + + return bind; +} + static tree_t elab_default_binding(tree_t inst, const elab_ctx_t *ctx) { // Default binding indication is described in LRM 93 section 5.2.2 @@ -589,7 +618,7 @@ static tree_t elab_default_binding(tree_t inst, const elab_ctx_t *ctx) vlog_node_t mod = vlog_from_object(obj); if (mod != NULL) { mod_cache_t *mc = elab_cached_module(mod, ctx); - return elab_mixed_binding(comp, mc, ctx); + return elab_mixed_binding(comp, mc); } tree_t entity = tree_from_object(obj); @@ -1298,6 +1327,7 @@ static void elab_verilog_module(tree_t bind, tree_t wrap, const elab_ctx_t *ctx) elab_decls(mc->block, &new_ctx); if (error_count() == 0) { + new_ctx.drivers = find_drivers(mc->block); elab_lower(b, mc->shape, &new_ctx); elab_stmts(mc->block, &new_ctx); } @@ -1761,6 +1791,60 @@ static void elab_psl(tree_t t, const elab_ctx_t *ctx) tree_add_stmt(ctx->out, t); } +static void elab_verilog_stmt(tree_t wrap, const elab_ctx_t *ctx) +{ + vlog_node_t v = tree_vlog(wrap); + switch (vlog_kind(v)) { + case V_MOD_INST: + { + ident_t modname = vlog_ident2(v); + ident_t libname = lib_name(ctx->library); + + text_buf_t *tb = tb_new(); + tb_istr(tb, libname); + tb_append(tb, '.'); + tb_istr(tb, modname); + tb_upcase(tb); + + ident_t qual = ident_new(tb_get(tb)); + + object_t *obj = lib_get_generic(ctx->library, qual); + if (obj == NULL) { + error_at(vlog_loc(v), "module %s not found in library %s", + istr(modname), istr(libname)); + return; + } + + vlog_node_t mod = vlog_from_object(obj); + if (mod == NULL) { + error_at(&obj->loc, "unit %s is not a Verilog module", istr(qual)); + return; + } + else if (vlog_ident2(mod) != modname) { + diag_t *d = diag_new(DIAG_ERROR, vlog_loc(v)); + diag_printf(d, "name of Verilog module %s in library unit %s " + "does not match name %s in module instance %s", + istr(vlog_ident2(mod)), istr(qual), istr(modname), + istr(vlog_ident(v))); + diag_hint(d, NULL, "this tool does not preserve case sensitivity " + "in module names"); + diag_emit(d); + return; + } + + mod_cache_t *mc = elab_cached_module(mod, ctx); + + tree_t bind = elab_verilog_binding(v, mc, ctx); + if (bind != NULL) + elab_verilog_module(bind, mc->wrap, ctx); + } + break; + default: + tree_add_stmt(ctx->out, wrap); + break; + } +} + static void elab_stmts(tree_t t, const elab_ctx_t *ctx) { const int nstmts = tree_stmts(t); @@ -1790,7 +1874,7 @@ static void elab_stmts(tree_t t, const elab_ctx_t *ctx) elab_psl(s, ctx); break; case T_VERILOG: - tree_add_stmt(ctx->out, s); + elab_verilog_stmt(s, ctx); break; default: fatal_trace("unexpected statement %s", tree_kind_str(tree_kind(s))); diff --git a/src/vlog/vlog-dump.c b/src/vlog/vlog-dump.c index e59fb7c2..522143a5 100644 --- a/src/vlog/vlog-dump.c +++ b/src/vlog/vlog-dump.c @@ -318,6 +318,27 @@ static void vlog_dump_gate_inst(vlog_node_t v, int indent) print_syntax(");\n"); } +static void vlog_dump_mod_inst(vlog_node_t v, int indent) +{ + tab(indent); + + print_syntax("%s ", istr(vlog_ident2(v))); + + print_syntax("%s ", istr(vlog_ident(v))); + + const int nparams = vlog_params(v); + if (nparams > 0) { + print_syntax("("); + for (int i = 0; i < nparams; i++) { + if (i > 0) print_syntax(","); + vlog_dump(vlog_param(v, i), 0); + } + print_syntax(")"); + } + + print_syntax(";\n"); +} + static void vlog_dump_strength(vlog_node_t v, int indent) { switch (vlog_subkind(v)) { @@ -398,6 +419,9 @@ void vlog_dump(vlog_node_t v, int indent) case V_GATE_INST: vlog_dump_gate_inst(v, indent); break; + case V_MOD_INST: + vlog_dump_mod_inst(v, indent); + break; case V_STRENGTH: vlog_dump_strength(v, indent); break; diff --git a/src/vlog/vlog-lower.c b/src/vlog/vlog-lower.c index 18ccce8e..5c31641d 100644 --- a/src/vlog/vlog-lower.c +++ b/src/vlog/vlog-lower.c @@ -858,6 +858,8 @@ static void vlog_lower_concurrent(unit_registry_t *ur, lower_unit_t *parent, case V_GATE_INST: vlog_lower_gate_inst(ur, parent, s); break; + case V_MOD_INST: + break; default: CANNOT_HANDLE(s); } diff --git a/src/vlog/vlog-node.c b/src/vlog/vlog-node.c index a8e49734..5529b152 100644 --- a/src/vlog/vlog-node.c +++ b/src/vlog/vlog-node.c @@ -97,6 +97,9 @@ static const imask_t has_map[V_LAST_NODE_KIND] = { // V_STRENGTH (I_SUBKIND), + + // V_MOD_INST + (I_IDENT | I_IDENT2 | I_PARAMS), }; static const char *kind_text_map[V_LAST_NODE_KIND] = { @@ -106,6 +109,7 @@ static const char *kind_text_map[V_LAST_NODE_KIND] = { "V_NET_DECL", "V_ASSIGN", "V_DIMENSION", "V_IF", "V_COND", "V_VAR_DECL", "V_DELAY_CONTROL", "V_BINARY", "V_BASSIGN", "V_UNARY", "V_GATE_INST", "V_STRENGTH", + "V_MOD_INST", }; static const change_allowed_t change_allowed[] = { diff --git a/src/vlog/vlog-node.h b/src/vlog/vlog-node.h index 86d84b6c..5be8ea63 100644 --- a/src/vlog/vlog-node.h +++ b/src/vlog/vlog-node.h @@ -69,6 +69,7 @@ typedef enum { V_UNARY, V_GATE_INST, V_STRENGTH, + V_MOD_INST, V_LAST_NODE_KIND } vlog_kind_t; diff --git a/src/vlog/vlog-parse.y b/src/vlog/vlog-parse.y index 0d44db8e..5f9c6282 100644 --- a/src/vlog/vlog-parse.y +++ b/src/vlog/vlog-parse.y @@ -138,7 +138,7 @@ static vlog_node_t make_strength(vlog_strength_t value, const loc_t *loc) %type seq_block system_task_enable string number %type decimal_number conditional_statement variable_type %type delay_control delay_value strength0 strength1 -%type pull_gate_instance port_identifier +%type pull_gate_instance port_identifier module_instance %type identifier hierarchical_identifier %type module_item_list module_port_list_opt module_item %type list_of_port_declarations module_item_list_opt @@ -150,6 +150,8 @@ static vlog_node_t make_strength(vlog_strength_t value, const loc_t *loc) %type list_of_variable_identifiers list_of_statements_opt %type gate_instantiation pulldown_strength pullup_strength %type port_declaration port_declaration_head +%type module_instantiation list_of_port_connections +%type list_of_port_connections_opt %type external_identifier %type net_type @@ -351,6 +353,43 @@ module_or_generate_item: | initial_construct { $$ = node_list_single($1); } | continuous_assign | gate_instantiation + | module_instantiation + ; + +module_instantiation: + identifier module_instance ';' + { + vlog_set_ident2($2, $1); + $$ = node_list_single($2); + } + ; + +module_instance: + identifier list_of_port_connections_opt + { + $$ = vlog_new(V_MOD_INST); + vlog_set_loc($$, &@$); + vlog_set_ident($$, $1); + + for (node_list_t *it = $2; it; it = it->next) + vlog_add_param($$, it->value); + + node_list_free($2); + } + ; + +list_of_port_connections_opt: + '(' list_of_port_connections ')' { $$ = $2; } + | /* empty */ { $$ = NULL; } + ; + +list_of_port_connections: + list_of_port_connections ',' port_reference + { + $$ = $1; + node_list_append(&$$, $3); + } + | port_reference { $$ = node_list_single($1); } ; module_or_generate_item_declaration: diff --git a/src/vlog/vlog-sem.c b/src/vlog/vlog-sem.c index 5733d90e..dfb0c6c5 100644 --- a/src/vlog/vlog-sem.c +++ b/src/vlog/vlog-sem.c @@ -314,6 +314,15 @@ static void vlog_check_gate_inst(vlog_node_t g) vlog_set_ident(g, ident_uniq("#gate")); } +static void vlog_check_mod_inst(vlog_node_t inst) +{ + const int nparams = vlog_params(inst); + for (int i = 0; i < nparams; i++) + vlog_check(vlog_param(inst, i)); + + vlog_insert_decl(inst); +} + void vlog_check(vlog_node_t v) { switch (vlog_kind(v)) { @@ -380,6 +389,9 @@ void vlog_check(vlog_node_t v) case V_GATE_INST: vlog_check_gate_inst(v); break; + case V_MOD_INST: + vlog_check_mod_inst(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 7e056192..084d2f7b 100644 --- a/test/dump/vlog1.v +++ b/test/dump/vlog1.v @@ -19,4 +19,5 @@ module mod2; end assign bus = 3; pullup (supply1, supply0) p1 (w); + mod u1 (w); endmodule // mod2 diff --git a/test/elab/vlog1.v b/test/elab/vlog1.v new file mode 100644 index 00000000..2f565583 --- /dev/null +++ b/test/elab/vlog1.v @@ -0,0 +1,13 @@ +module sub1(x, y); + input x; + output y; +endmodule // sub1 + +module vlog1; + wire x, y; + + sub1 u1 (x, y); // OK + SUB1 u2 (x, y); // Error + sub1 u3 (x); // Error + bad u4 (x); // Error +endmodule // vlog1 diff --git a/test/regress/gold/issue808.txt b/test/regress/gold/issue808.txt index 6e8183b7..2c12819a 100644 --- a/test/regress/gold/issue808.txt +++ b/test/regress/gold/issue808.txt @@ -1 +1 @@ -0 1 +0 1 0 diff --git a/test/regress/issue808.v b/test/regress/issue808.v index 462dde0a..e27d3c5e 100644 --- a/test/regress/issue808.v +++ b/test/regress/issue808.v @@ -1,12 +1,20 @@ module issue808; supply0 gnd; supply1 vcc; + wire out; assign gnd = vcc; + GND uut (out); + initial begin - $display("%x %x", gnd, vcc); + #1 $display("%x %x %x", gnd, vcc, out); $finish; end endmodule // issue808 + +module GND (Y); + output Y; + supply0 Y ; +endmodule // GND diff --git a/test/test_dump.c b/test/test_dump.c index 971ba22a..db63626f 100644 --- a/test/test_dump.c +++ b/test/test_dump.c @@ -488,6 +488,7 @@ START_TEST(test_vlog1) " end\n" " assign bus = 5'b11;\n" " pullup (supply1,supply0) p1 (w);\n" + " mod u1 (w);\n" "endmodule // mod2\n\n"); tb_rewind(tb); diff --git a/test/test_elab.c b/test/test_elab.c index 944bcae6..49cb1683 100644 --- a/test/test_elab.c +++ b/test/test_elab.c @@ -1798,6 +1798,36 @@ START_TEST(test_jcore2) } END_TEST +START_TEST(test_vlog1) +{ +#ifdef ENABLE_VERILOG + const error_t expect[] = { + { 10, "name of Verilog module sub1 in library unit WORK.SUB1 does " + "not match name SUB1 in module instance u2" }, + { 11, "expected 2 port connections for module WORK.SUB1 but found 1" }, + { 12, "module bad not found in library WORK" }, + { -1, NULL } + }; + expect_errors(expect); + + analyse_file(TESTDIR "/elab/vlog1.v", NULL, NULL); + + object_t *obj = lib_get_generic(lib_work(), ident_new("WORK.VLOG1")); + ck_assert_ptr_nonnull(obj); + + unit_registry_t *ur = get_registry(); + jit_t *j = jit_new(ur); + + tree_t top = elab(obj, j, ur, NULL); + fail_unless(top == NULL); + + jit_free(j); + + check_expected_errors(); +#endif +} +END_TEST + Suite *get_elab_tests(void) { Suite *s = suite_create("elab"); @@ -1894,6 +1924,7 @@ Suite *get_elab_tests(void) tcase_add_test(tc, test_bounds7); tcase_add_test(tc, test_bounds11); tcase_add_test(tc, test_jcore2); + tcase_add_test(tc, test_vlog1); suite_add_tcase(s, tc); return s; -- 2.39.2