From cdcac65bd38bde61c512c394af7fe4656f2bbb55 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Thu, 29 Feb 2024 19:24:23 +0000 Subject: [PATCH] Verilog bit selects --- lib/nvc/verilog-body.vhd | 34 ++++++++++++ lib/nvc/verilog.vhd | 5 +- src/common.c | 5 +- src/common.h | 3 +- src/elab.c | 83 ++++++++++++++++++++++++++++- src/lexer.l | 4 ++ src/scan.c | 3 +- src/scan.h | 3 ++ src/vlog/vlog-defs.h | 1 + src/vlog/vlog-dump.c | 15 ++++++ src/vlog/vlog-lower.c | 97 ++++++++++++++++++++++++--------- src/vlog/vlog-node.c | 7 ++- src/vlog/vlog-node.h | 1 + src/vlog/vlog-number.c | 15 ++++++ src/vlog/vlog-number.h | 1 + src/vlog/vlog-parse.y | 109 ++++++++++++++++++++++++++++---------- src/vlog/vlog-sem.c | 107 ++++++++++++++++++++++++++++++++++++- src/vlog/vlog-trans.c | 2 +- test/dump/vlog1.v | 1 + test/regress/testlist.txt | 1 + test/regress/vlog9.v | 89 +++++++++++++++++++++++++++++++ test/test_dump.c | 1 + test/test_vlog.c | 4 +- test/vlog/ports.v | 9 +++- 24 files changed, 533 insertions(+), 67 deletions(-) create mode 100644 test/regress/vlog9.v diff --git a/lib/nvc/verilog-body.vhd b/lib/nvc/verilog-body.vhd index d1437c96..1a06c796 100644 --- a/lib/nvc/verilog-body.vhd +++ b/lib/nvc/verilog-body.vhd @@ -68,6 +68,28 @@ package body verilog is end case; end function; + function to_logic (value : t_net_array) return t_packed_logic is + constant length : natural := value'length; + alias a_value : t_net_array(1 to length) is value; + variable result : t_packed_logic(1 to length); + begin + for i in 1 to length loop + result(i) := to_logic(a_value(i)); + end loop; + return result; + end function; + + function to_logic (value : t_resolved_net_array) return t_packed_logic is + constant length : natural := value'length; + alias a_value : t_resolved_net_array(1 to length) is value; + variable result : t_packed_logic(1 to length); + begin + for i in 1 to length loop + result(i) := to_logic(a_value(i)); + end loop; + return result; + end function; + function to_net_value (value : t_logic) return t_net_value is begin case value is @@ -89,6 +111,18 @@ package body verilog is return result; end function; + function to_net_value (value : t_packed_logic) return t_resolved_net_array is + constant length : natural := value'length; + alias a_value : t_packed_logic(1 to length) is value; + variable result : t_resolved_net_array(1 to length); + begin + for i in 1 to length loop + result(i) := to_net_value(a_value(i)); + end loop; + return result; + end function; + + function to_integer (value : t_packed_logic) return t_int64 is alias v : t_packed_logic(0 to value'length - 1) is value; variable r : t_int64 := 0; diff --git a/lib/nvc/verilog.vhd b/lib/nvc/verilog.vhd index 5552f446..a7c0f5bc 100644 --- a/lib/nvc/verilog.vhd +++ b/lib/nvc/verilog.vhd @@ -39,12 +39,15 @@ package verilog is subtype t_resolved_net is resolved t_net_value; - type t_resolved_packed_net is array (natural range <>) of t_resolved_net; + type t_resolved_net_array is array (natural range <>) of t_resolved_net; function to_logic (value : t_net_value) return t_logic; + function to_logic (value : t_net_array) return t_packed_logic; + function to_logic (value : t_resolved_net_array) return t_packed_logic; function to_net_value (value : t_logic) return t_net_value; function to_net_value (value : t_packed_logic) return t_net_array; + function to_net_value (value : t_packed_logic) return t_resolved_net_array; function to_integer (value : t_packed_logic) return t_int64; diff --git a/src/common.c b/src/common.c index 42f478de..04f118b0 100644 --- a/src/common.c +++ b/src/common.c @@ -1311,7 +1311,7 @@ static tree_t cached_verilog(void) type_t verilog_type(verilog_type_t which) { - static type_t cache[VERILOG_RESOLVED_PACKED_NET + 1] = {}; + static type_t cache[VERILOG_RESOLVED_NET_ARRAY + 1] = {}; assert(which < ARRAY_LEN(cache)); if (cache[which] == NULL) { @@ -1320,8 +1320,9 @@ type_t verilog_type(verilog_type_t which) "T_PACKED_LOGIC", "T_INT64", "T_NET_VALUE", + "T_NET_ARRAY", "T_RESOLVED_NET", - "T_RESOLVED_PACKED_NET", + "T_RESOLVED_NET_ARRAY", }; tree_t d = search_decls(cached_verilog(), ident_new(names[which]), 0); diff --git a/src/common.h b/src/common.h index fa74f31e..c70fd928 100644 --- a/src/common.h +++ b/src/common.h @@ -175,8 +175,9 @@ typedef enum { VERILOG_PACKED_LOGIC, VERILOG_INT64, VERILOG_NET_VALUE, + VERILOG_NET_ARRAY, VERILOG_RESOLVED_NET, - VERILOG_RESOLVED_PACKED_NET, + VERILOG_RESOLVED_NET_ARRAY, } verilog_type_t; type_t verilog_type(verilog_type_t which); diff --git a/src/elab.c b/src/elab.c index dfa0ac71..669f1baa 100644 --- a/src/elab.c +++ b/src/elab.c @@ -557,6 +557,46 @@ static tree_t elab_mixed_binding(tree_t comp, mod_cache_t *mc) return bind; } +static tree_t elab_verilog_conversion(type_t from, type_t to) +{ + static struct { + const verilog_type_t from_id; + const verilog_type_t to_id; + const char *const func; + type_t from; + type_t to; + tree_t decl; + } table[] = { + { VERILOG_NET_VALUE, VERILOG_LOGIC, + "NVC.VERILOG.TO_LOGIC(" T_NET_VALUE ")" T_LOGIC }, + { VERILOG_NET_ARRAY, VERILOG_PACKED_LOGIC, + "NVC.VERILOG.TO_LOGIC(" T_NET_ARRAY ")" T_PACKED_LOGIC }, + { VERILOG_RESOLVED_NET_ARRAY, VERILOG_PACKED_LOGIC, + "NVC.VERILOG.TO_LOGIC(" T_RESOLVED_NET_ARRAY ")" T_PACKED_LOGIC }, + { VERILOG_LOGIC, VERILOG_NET_VALUE, + "NVC.VERILOG.TO_NET_VALUE(" T_LOGIC ")" T_NET_VALUE }, + { VERILOG_PACKED_LOGIC, VERILOG_NET_ARRAY, + "NVC.VERILOG.TO_NET_VALUE(" T_PACKED_LOGIC ")" T_NET_ARRAY }, + { VERILOG_PACKED_LOGIC, VERILOG_RESOLVED_NET_ARRAY, + "NVC.VERILOG.TO_NET_VALUE(" T_PACKED_LOGIC ")" T_RESOLVED_NET_ARRAY }, + }; + + INIT_ONCE({ + for (int i = 0; i < ARRAY_LEN(table); i++) { + table[i].from = verilog_type(table[i].from_id); + table[i].to = verilog_type(table[i].to_id); + table[i].decl = verilog_func(ident_new(table[i].func)); + } + }); + + for (int i = 0; i < ARRAY_LEN(table); i++) { + if (type_eq(table[i].from, from) && type_eq(table[i].to, to)) + return table[i].decl; + } + + return NULL; +} + static tree_t elab_verilog_binding(vlog_node_t inst, mod_cache_t *mc, const elab_ctx_t *ctx) { @@ -577,6 +617,7 @@ static tree_t elab_verilog_binding(vlog_node_t inst, mod_cache_t *mc, return NULL; } + bool have_named = false; for (int i = 0; i < nports; i++) { vlog_node_t conn = vlog_param(inst, i); assert(vlog_kind(conn) == V_REF); @@ -584,7 +625,47 @@ static tree_t elab_verilog_binding(vlog_node_t inst, mod_cache_t *mc, tree_t decl = search_decls(ctx->out, vlog_ident(conn), 0); assert(decl != NULL); - add_param(bind, make_ref(decl), P_POS, NULL); + tree_t port = tree_port(mc->block, i); + + type_t dtype = tree_type(decl); + type_t ptype = tree_type(port); + + if (type_eq(dtype, ptype)) { + if (have_named) + abort(); + else + add_param(bind, make_ref(decl), P_POS, NULL); + } + else if (tree_subkind(port) == PORT_IN) { + tree_t func = elab_verilog_conversion(dtype, ptype); + assert(func != NULL); + + tree_t conv = tree_new(T_CONV_FUNC); + tree_set_loc(conv, vlog_loc(conn)); + tree_set_ref(conv, func); + tree_set_ident(conv, tree_ident(func)); + tree_set_type(conv, type_result(tree_type(func))); + tree_set_value(conv, make_ref(decl)); + + if (have_named) + add_param(bind, conv, P_NAMED, make_ref(port)); + else + add_param(bind, conv, P_POS, NULL); + } + else { + tree_t func = elab_verilog_conversion(ptype, dtype); + assert(func != NULL); + + tree_t conv = tree_new(T_CONV_FUNC); + tree_set_loc(conv, vlog_loc(conn)); + tree_set_ref(conv, func); + tree_set_ident(conv, tree_ident(func)); + tree_set_type(conv, type_result(tree_type(func))); + tree_set_value(conv, make_ref(port)); + + add_param(bind, make_ref(decl), P_NAMED, conv); + have_named = true; + } } return bind; diff --git a/src/lexer.l b/src/lexer.l index 48317ad3..7951c4b3 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -120,6 +120,7 @@ TRANSLATE_ON {PRAGMA}(?i:pragma)[ \t]+(?i:translate_on).* PSL_COMMENT {PRAGMA}(?i:psl)[ \t]+ PSL_CONT ^{SPACE}*({PSL_COMMENT}|"--") UTF8_MB [\x80-\xff][\x80-\xbf]{1,3} +VLOG_NUMBER {INTEGER}?\'[bhd]{HEX} %x COMMENT C_COMMENT PSL VLOG @@ -545,11 +546,14 @@ UNION ?i:union "!=" { return tLOGNEQ; } "===" { return tCASEEQ; } "!==" { return tCASENEQ; } +"(*" { return tATTRBEGIN; } +"*)" { return tATTREND; } {SYSTASK} { yylval.str = xstrdup(yytext); return tSYSTASK; } {VLOG_ID} { yylval.str = xstrdup(yytext); return tID; } {INTEGER} { yylval.str = xstrdup(yytext); return tUNSIGNED; } {VLOG_STRING} { return escape_verilog_string(yytext); } +{VLOG_NUMBER} { yylval.str = xstrdup(yytext); return tNUMBER; } {DECIMAL} { return parse_decimal_literal(yytext); } {BASED} { return parse_based_literal(yytext); } diff --git a/src/scan.c b/src/scan.c index 5c30b259..823cc698 100644 --- a/src/scan.c +++ b/src/scan.c @@ -228,7 +228,8 @@ const char *token_str(token_t tok) "&&", "within", "system task", "view", "private", "prev", "stable", "rose", "fell", "ended", "nondet", "nondetv", "union", "translate on", "translate off", "until!", "until_", "until_!", "`timescale" - "supply0", "supply1", "pulldown", "pullup", "===", "!==", + "supply0", "supply1", "pulldown", "pullup", "===", "!==", "==", "!=", + "(*", "*)", "number", }; if (tok >= 200 && tok - 200 < ARRAY_LEN(token_strs)) diff --git a/src/scan.h b/src/scan.h index 513f8796..6b53dd47 100644 --- a/src/scan.h +++ b/src/scan.h @@ -301,5 +301,8 @@ bool is_scanned_as_psl(void); #define tCASENEQ 405 #define tLOGEQ 406 #define tLOGNEQ 407 +#define tATTRBEGIN 408 +#define tATTREND 409 +#define tNUMBER 410 #endif // _SCAN_H diff --git a/src/vlog/vlog-defs.h b/src/vlog/vlog-defs.h index b420405b..e8b04230 100644 --- a/src/vlog/vlog-defs.h +++ b/src/vlog/vlog-defs.h @@ -23,6 +23,7 @@ #define T_INT64 "19NVC.VERILOG.T_INT64" #define T_NET_VALUE "23NVC.VERILOG.T_NET_VALUE" #define T_NET_ARRAY "23NVC.VERILOG.T_NET_ARRAY" +#define T_RESOLVED_NET_ARRAY "32NVC.VERILOG.T_RESOLVED_NET_ARRAY" typedef enum { _X = 0, _SUPPLY0, _STRONG0, _PULL0, _LARGE0, _WEAK0, _MEDIUM0, diff --git a/src/vlog/vlog-dump.c b/src/vlog/vlog-dump.c index 522143a5..e8e11c76 100644 --- a/src/vlog/vlog-dump.c +++ b/src/vlog/vlog-dump.c @@ -353,6 +353,18 @@ static void vlog_dump_strength(vlog_node_t v, int indent) } } +static void vlog_dump_bit_select(vlog_node_t v, int indent) +{ + print_syntax("%s", istr(vlog_ident(v))); + + const int nparams = vlog_params(v); + for (int i = 0; i < nparams; i++) { + print_syntax("["); + vlog_dump(vlog_param(v, i), 0); + print_syntax("]"); + } +} + void vlog_dump(vlog_node_t v, int indent) { switch (vlog_kind(v)) { @@ -425,6 +437,9 @@ void vlog_dump(vlog_node_t v, int indent) case V_STRENGTH: vlog_dump_strength(v, indent); break; + case V_BIT_SELECT: + vlog_dump_bit_select(v, indent); + break; default: print_syntax("\n"); fflush(stdout); diff --git a/src/vlog/vlog-lower.c b/src/vlog/vlog-lower.c index 11d127a2..a9f65fc5 100644 --- a/src/vlog/vlog-lower.c +++ b/src/vlog/vlog-lower.c @@ -163,9 +163,15 @@ static vcode_reg_t vlog_lower_to_integer(lower_unit_t *lu, vcode_reg_t reg) return emit_fcall(func, vint64, vint64, args, ARRAY_LEN(args)); } -static vcode_reg_t vlog_lower_to_bool(lower_unit_t *lu, vcode_reg_t reg) +static vcode_reg_t vlog_lower_to_offset(lower_unit_t *lu, vcode_reg_t reg) { + vcode_type_t voffset = vtype_offset(); + vcode_reg_t int_reg = vlog_lower_to_integer(lu, reg); + return emit_cast(voffset, voffset, int_reg); +} +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: if (vtype_eq(vcode_reg_type(reg), vtype_bool())) @@ -182,6 +188,36 @@ static vcode_reg_t vlog_lower_to_bool(lower_unit_t *lu, vcode_reg_t reg) } } +static vcode_reg_t vlog_lower_to_logic(lower_unit_t *lu, vcode_reg_t reg) +{ + vcode_type_t vlogic = vlog_logic_type(); + + switch (vcode_reg_kind(reg)) { + case VCODE_TYPE_INT: + if (vtype_eq(vcode_reg_type(reg), vlogic)) + return reg; + else { + vcode_reg_t context_reg = vlog_helper_package(); + vcode_reg_t args[] = { context_reg, reg }; + ident_t func = ident_new("NVC.VERILOG.TO_LOGIC(" + T_NET_VALUE ")" T_LOGIC); + return emit_fcall(func, vlogic, vlogic, args, ARRAY_LEN(args)); + } + case VCODE_TYPE_UARRAY: + { + vcode_type_t vpacked = vlog_packed_logic_type(); + vcode_reg_t context_reg = vlog_helper_package(); + vcode_reg_t args[] = { context_reg, reg }; + ident_t func = ident_new("NVC.VERILOG.TO_LOGIC(" + T_NET_ARRAY ")" T_PACKED_LOGIC); + return emit_fcall(func, vpacked, vlogic, args, ARRAY_LEN(args)); + } + default: + vcode_dump(); + fatal_trace("cannot convert r%d to logic", reg); + } +} + static vcode_reg_t vlog_lower_integer(lower_unit_t *lu, vlog_node_t expr) { if (vlog_kind(expr) == V_NUMBER) { @@ -217,32 +253,45 @@ static vcode_reg_t vlog_lower_decl_bounds(lower_unit_t *lu, vlog_node_t decl, return emit_wrap(data, dims, nranges); } +static vcode_reg_t vlog_lower_lvalue_ref(lower_unit_t *lu, vlog_node_t ref) +{ + vlog_node_t decl = vlog_ref(ref); + if (vlog_kind(decl) == V_PORT_DECL) + decl = vlog_ref(decl); + + int hops; + vcode_var_t var = lower_search_vcode_obj(decl, lu, &hops); + assert(var != VCODE_INVALID_VAR); + + vcode_reg_t nets_reg; + if (hops == 0) + nets_reg = emit_load(var); + else + nets_reg = emit_load_indirect(emit_var_upref(hops, var)); + + if (vcode_reg_kind(nets_reg) != VCODE_TYPE_UARRAY && vlog_ranges(decl) > 0) + return vlog_lower_decl_bounds(lu, decl, nets_reg); + else + return nets_reg; +} + static vcode_reg_t vlog_lower_lvalue(lower_unit_t *lu, vlog_node_t v) { PUSH_DEBUG_INFO(v); switch (vlog_kind(v)) { case V_REF: + return vlog_lower_lvalue_ref(lu, v); + case V_BIT_SELECT: { - vlog_node_t decl = vlog_ref(v); - if (vlog_kind(decl) == V_PORT_DECL) - decl = vlog_ref(decl); - - int hops; - vcode_var_t var = lower_search_vcode_obj(decl, lu, &hops); - assert(var != VCODE_INVALID_VAR); + vcode_reg_t wrap_reg = vlog_lower_lvalue_ref(lu, v); + vcode_reg_t nets_reg = emit_unwrap(wrap_reg); - vcode_reg_t nets_reg; - if (hops == 0) - nets_reg = emit_load(var); - else - nets_reg = emit_load_indirect(emit_var_upref(hops, var)); + assert(vlog_params(v) == 1); + vcode_reg_t p0_reg = vlog_lower_rvalue(lu, vlog_param(v, 0)); + vcode_reg_t off_reg = vlog_lower_to_offset(lu, p0_reg); - if (vcode_reg_kind(nets_reg) != VCODE_TYPE_UARRAY - && vlog_ranges(decl) > 0) - return vlog_lower_decl_bounds(lu, decl, nets_reg); - else - return nets_reg; + return emit_array_ref(nets_reg, off_reg); } default: CANNOT_HANDLE(v); @@ -357,6 +406,8 @@ static vcode_reg_t vlog_lower_binary(lower_unit_t *lu, vlog_binary_t op, static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v) { + PUSH_DEBUG_INFO(v); + switch (vlog_kind(v)) { case V_REF: { @@ -394,14 +445,8 @@ static vcode_reg_t vlog_lower_rvalue(lower_unit_t *lu, vlog_node_t v) resolved_reg = emit_load_indirect(data_reg); } - if (vlog_is_net(decl)) { - vcode_reg_t context_reg = vlog_helper_package(); - vcode_reg_t args[] = { context_reg, resolved_reg }; - vcode_type_t vlogic = vlog_logic_type(); - ident_t func = ident_new("NVC.VERILOG.TO_LOGIC(" - T_NET_VALUE ")" T_LOGIC); - return emit_fcall(func, vlogic, vlogic, args, ARRAY_LEN(args)); - } + if (vlog_is_net(decl)) + return vlog_lower_to_logic(lu, resolved_reg); else return resolved_reg; } diff --git a/src/vlog/vlog-node.c b/src/vlog/vlog-node.c index 5529b152..ccce32f5 100644 --- a/src/vlog/vlog-node.c +++ b/src/vlog/vlog-node.c @@ -30,7 +30,7 @@ static const imask_t has_map[V_LAST_NODE_KIND] = { (I_IDENT | I_PORTS | I_STMTS | I_DECLS | I_IDENT2), // V_PORT_DECL - (I_IDENT | I_SUBKIND | I_IDENT2 | I_REF), + (I_IDENT | I_SUBKIND | I_IDENT2 | I_REF | I_RANGES), // V_REF (I_IDENT | I_REF), @@ -100,6 +100,9 @@ static const imask_t has_map[V_LAST_NODE_KIND] = { // V_MOD_INST (I_IDENT | I_IDENT2 | I_PARAMS), + + // V_BIT_SELECT + (I_IDENT | I_REF | I_PARAMS), }; static const char *kind_text_map[V_LAST_NODE_KIND] = { @@ -109,7 +112,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", + "V_MOD_INST", "V_BIT_SELECT", }; static const change_allowed_t change_allowed[] = { diff --git a/src/vlog/vlog-node.h b/src/vlog/vlog-node.h index 44c3865f..185bbb34 100644 --- a/src/vlog/vlog-node.h +++ b/src/vlog/vlog-node.h @@ -70,6 +70,7 @@ typedef enum { V_GATE_INST, V_STRENGTH, V_MOD_INST, + V_BIT_SELECT, V_LAST_NODE_KIND } vlog_kind_t; diff --git a/src/vlog/vlog-number.c b/src/vlog/vlog-number.c index 8f373902..c8f867aa 100644 --- a/src/vlog/vlog-number.c +++ b/src/vlog/vlog-number.c @@ -320,6 +320,21 @@ number_t number_pack(const uint8_t *bits, unsigned width) } } +bool number_equal(number_t a, number_t b) +{ + assert(a.common.tag == b.common.tag); // TODO + + switch (a.common.tag) { + case TAG_INTEGER: + return a.intg.packed == b.intg.packed; + case TAG_SMALLNUM: + return a.small.packed == b.small.packed; + default: + DEBUG_ONLY(fatal_trace("invalid number tag %x", a.common.tag)); + return false; + } +} + void number_write(number_t val, fbuf_t *f) { assert(val.common.tag != TAG_BIGNUM); diff --git a/src/vlog/vlog-number.h b/src/vlog/vlog-number.h index c54d7b4f..6e2d1aed 100644 --- a/src/vlog/vlog-number.h +++ b/src/vlog/vlog-number.h @@ -83,6 +83,7 @@ int64_t number_integer(number_t val); unsigned number_width(number_t val); vlog_logic_t number_bit(number_t val, unsigned n); number_t number_pack(const uint8_t *bits, unsigned width); +bool number_equal(number_t a, number_t b); void number_write(number_t val, fbuf_t *f); number_t number_read(fbuf_t *f); diff --git a/src/vlog/vlog-parse.y b/src/vlog/vlog-parse.y index 5f0f467f..80c4de73 100644 --- a/src/vlog/vlog-parse.y +++ b/src/vlog/vlog-parse.y @@ -133,7 +133,7 @@ static vlog_node_t make_strength(vlog_strength_t value, const loc_t *loc) %type procedural_timing_control_statement %type lvalue event_control event_expression %type nonblocking_assignment delay_or_event_control -%type port_reference blocking_assignment +%type port_reference blocking_assignment range_opt %type port initial_construct net_assignment %type seq_block system_task_enable string number %type decimal_number conditional_statement variable_type @@ -185,6 +185,9 @@ static vlog_node_t make_strength(vlog_strength_t value, const loc_t *loc) %token tCASENEQ 405 "!==" %token tLOGEQ 406 "==" %token tLOGNEQ 407 "!=" +%token tATTRBEGIN 408 "(*" +%token tATTREND 409 "*)" +%token tNUMBER 410 "number" %token tEOF 0 "end of file" @@ -221,29 +224,36 @@ description: { root = vlog_new(V_MODULE); } module_declaration { YYACCEPT; } | tEOF { root = NULL; } ; +attr_spec: tATTRBEGIN identifier tATTREND + ; + +attr_list_opt: attr_list_opt attr_spec + | /* empty */ + ; + module_declaration: - tMODULE external_identifier module_port_list_opt ';' - module_item_list_opt tENDMODULE + attr_list_opt tMODULE external_identifier + module_port_list_opt ';' module_item_list_opt tENDMODULE { ident_t qual = ident_prefix(lib_name(lib_work()), - $2.right, '.'); + $3.right, '.'); $$ = root; vlog_set_ident($$, qual); - vlog_set_ident2($$, $2.left); + vlog_set_ident2($$, $3.left); vlog_set_loc($$, &@$); - for (node_list_t *it = $3; it; it = it->next) + for (node_list_t *it = $4; it; it = it->next) add_port($$, it->value); - node_list_free($3); + node_list_free($4); - for (node_list_t *it = $5; it; it = it->next) { + for (node_list_t *it = $6; it; it = it->next) { if (is_decl(it->value)) vlog_add_decl($$, it->value); else vlog_add_stmt($$, it->value); } - node_list_free($5); + node_list_free($6); } ; @@ -297,32 +307,54 @@ list_of_port_declarations: | port_declaration_head { $$ = $1; } ; +range_opt: '[' expression ':' expression ']' + { + $$ = vlog_new(V_DIMENSION); + vlog_set_loc($$, &@$); + vlog_set_subkind($$, V_DIM_PACKED); + vlog_set_left($$, $2); + vlog_set_right($$, $4); + } + | /* empty */ { $$ = NULL; } + ; + port_declaration_head: - tINPUT port_identifier + tINPUT range_opt port_identifier { - vlog_set_subkind($2, V_PORT_INPUT); - $$ = node_list_single($2); + vlog_set_subkind($3, V_PORT_INPUT); + if ($2 != NULL) + vlog_add_range($3, $2); + + $$ = node_list_single($3); } - | tOUTPUT port_identifier + | tOUTPUT range_opt port_identifier { - vlog_set_subkind($2, V_PORT_OUTPUT); - $$ = node_list_single($2); + vlog_set_subkind($3, V_PORT_OUTPUT); + if ($2 != NULL) + vlog_add_range($3, $2); + + $$ = node_list_single($3); } - | tINOUT port_identifier + | tINOUT range_opt port_identifier { - vlog_set_subkind($2, V_PORT_INOUT); - $$ = node_list_single($2); + vlog_set_subkind($3, V_PORT_INOUT); + if ($2 != NULL) + vlog_add_range($3, $2); + + $$ = node_list_single($3); } - | tOUTPUT tREG port_identifier + | tOUTPUT tREG range_opt port_identifier { - vlog_set_subkind($3, V_PORT_OUTPUT); + vlog_set_subkind($4, V_PORT_OUTPUT); + if ($3 != NULL) + vlog_add_range($4, $3); $$ = NULL; - node_list_append(&$$, $3); + node_list_append(&$$, $4); vlog_node_t reg = vlog_new(V_VAR_DECL); vlog_set_loc(reg, &@$); - vlog_set_ident(reg, vlog_ident($3)); + vlog_set_ident(reg, vlog_ident($4)); node_list_append(&$$, reg); } @@ -357,12 +389,12 @@ module_item: ; module_or_generate_item: - module_or_generate_item_declaration - | always_construct { $$ = node_list_single($1); } - | initial_construct { $$ = node_list_single($1); } - | continuous_assign - | gate_instantiation - | module_instantiation + attr_list_opt module_or_generate_item_declaration { $$ = $2; } + | attr_list_opt always_construct { $$ = node_list_single($2); } + | attr_list_opt initial_construct { $$ = node_list_single($2); } + | attr_list_opt continuous_assign { $$ = $2; } + | attr_list_opt gate_instantiation { $$ = $2; } + | attr_list_opt module_instantiation { $$ = $2; } ; module_instantiation: @@ -742,6 +774,13 @@ lvalue: hierarchical_identifier vlog_set_loc($$, &@$); vlog_set_ident($$, $1); } + | hierarchical_identifier '[' expression ']' + { + $$ = vlog_new(V_BIT_SELECT); + vlog_set_ident($$, $1); + vlog_add_param($$, $3); + vlog_set_loc($$, &@$); + } ; expression: primary @@ -839,10 +878,24 @@ primary: hierarchical_identifier vlog_set_ident($$, $1); vlog_set_loc($$, &@$); } + | hierarchical_identifier '[' expression ']' + { + $$ = vlog_new(V_BIT_SELECT); + vlog_set_ident($$, $1); + vlog_add_param($$, $3); + vlog_set_loc($$, &@$); + } | number ; number: decimal_number + | tNUMBER + { + $$ = vlog_new(V_NUMBER); + vlog_set_loc($$, &@$); + vlog_set_number($$, number_new($1)); + free($1); + } ; decimal_number: tUNSIGNED diff --git a/src/vlog/vlog-sem.c b/src/vlog/vlog-sem.c index dfb0c6c5..75b3db21 100644 --- a/src/vlog/vlog-sem.c +++ b/src/vlog/vlog-sem.c @@ -21,6 +21,7 @@ #include "hash.h" #include "ident.h" #include "vlog/vlog-node.h" +#include "vlog/vlog-number.h" #include "vlog/vlog-phase.h" #include "vlog/vlog-util.h" @@ -68,6 +69,17 @@ static void pop_scope(void) free(tmp); } +static bool constant_equal(vlog_node_t a, vlog_node_t b) +{ + if (vlog_kind(a) != V_NUMBER || vlog_kind(b) != V_NUMBER) + return false; + + number_t an = vlog_number(a); + number_t bn = vlog_number(b); + + return number_equal(an, bn); +} + static void vlog_insert_decl(vlog_node_t v) { ident_t id = vlog_ident(v); @@ -86,6 +98,32 @@ static void vlog_insert_decl(vlog_node_t v) hash_put(top_scope->symbols, id, v); } +static void vlog_check_const_expr(vlog_node_t expr) +{ + vlog_check(expr); + + switch (vlog_kind(expr)) { + case V_NUMBER: + break; + case V_REF: + if (vlog_has_ref(expr)) { + vlog_node_t decl = vlog_ref(expr); + + diag_t *d = diag_new(DIAG_ERROR, vlog_loc(expr)); + diag_printf(d, "cannot reference %s '%s' in constant expression", + vlog_is_net(decl) ? "net" : "variable", + istr(vlog_ident(decl))); + diag_hint(d, vlog_loc(decl), "%s declared here", + istr(vlog_ident(decl))); + diag_emit(d); + } + break; + default: + error_at(vlog_loc(expr), "expression is not a constant"); + break; + } +} + static void vlog_check_ref(vlog_node_t ref) { ident_t id = vlog_ident(ref); @@ -247,14 +285,46 @@ static void vlog_check_if(vlog_node_t stmt) } } +static void vlog_check_consistent(vlog_node_t a, vlog_node_t b) +{ + const int aranges = vlog_ranges(a); + const int branges = vlog_ranges(b); + + assert(aranges == branges); + + for (int i = 0; i < aranges; i++) { + vlog_node_t ar = vlog_range(a, i); + vlog_node_t br = vlog_range(b, i); + + vlog_node_t aleft = vlog_left(ar); + vlog_node_t bleft = vlog_left(br); + + vlog_node_t aright = vlog_right(ar); + vlog_node_t bright = vlog_right(br); + + if (!constant_equal(aleft, bleft) || !constant_equal(aright, bright)) { + diag_t *d = diag_new(DIAG_ERROR, vlog_loc(br)); + diag_printf(d, "inconsistent dimensions for '%s'", + istr(vlog_ident(b))); + diag_hint(d, vlog_loc(a), "earlier declaration here"); + diag_emit(d); + } + } +} + static void vlog_check_port_decl(vlog_node_t port) { + const int nranges = vlog_ranges(port); + for (int i = 0; i < nranges; i++) + vlog_check(vlog_range(port, i)); + ident_t id = vlog_ident(port); vlog_node_t exist = hash_get(top_scope->symbols, id); if (exist != NULL) { const vlog_kind_t kind = vlog_kind(exist); if (kind == V_VAR_DECL || kind == V_NET_DECL) { vlog_set_ref(port, exist); + vlog_check_consistent(exist, port); hash_put(top_scope->symbols, id, port); return; } @@ -265,20 +335,32 @@ static void vlog_check_port_decl(vlog_node_t port) static void vlog_check_net_decl(vlog_node_t net) { + const int nranges = vlog_ranges(net); + for (int i = 0; i < nranges; i++) + vlog_check(vlog_range(net, i)); + vlog_node_t exist = hash_get(top_scope->symbols, vlog_ident(net)); if (exist != NULL && vlog_kind(exist) == V_PORT_DECL - && !vlog_has_ref(exist)) + && !vlog_has_ref(exist)) { vlog_set_ref(exist, net); + vlog_check_consistent(exist, net); + } else vlog_insert_decl(net); } static void vlog_check_var_decl(vlog_node_t var) { + const int nranges = vlog_ranges(var); + for (int i = 0; i < nranges; i++) + vlog_check(vlog_range(var, i)); + vlog_node_t exist = hash_get(top_scope->symbols, vlog_ident(var)); if (exist != NULL && vlog_kind(exist) == V_PORT_DECL - && !vlog_has_ref(exist)) + && !vlog_has_ref(exist)) { vlog_set_ref(exist, var); + vlog_check_consistent(exist, var); + } else vlog_insert_decl(var); } @@ -323,6 +405,21 @@ static void vlog_check_mod_inst(vlog_node_t inst) vlog_insert_decl(inst); } +static void vlog_check_dimension(vlog_node_t dim) +{ + vlog_check_const_expr(vlog_left(dim)); + vlog_check_const_expr(vlog_right(dim)); +} + +static void vlog_check_bit_select(vlog_node_t bsel) +{ + vlog_check_ref(bsel); + + const int nparams = vlog_params(bsel); + for (int i = 0; i < nparams; i++) + vlog_check(vlog_param(bsel, i)); +} + void vlog_check(vlog_node_t v) { switch (vlog_kind(v)) { @@ -392,6 +489,12 @@ void vlog_check(vlog_node_t v) case V_MOD_INST: vlog_check_mod_inst(v); break; + case V_DIMENSION: + vlog_check_dimension(v); + break; + case V_BIT_SELECT: + vlog_check_bit_select(v); + break; default: fatal_at(vlog_loc(v), "cannot check verilog node %s", vlog_kind_str(vlog_kind(v))); diff --git a/src/vlog/vlog-trans.c b/src/vlog/vlog-trans.c index a6b132e0..08502bcb 100644 --- a/src/vlog/vlog-trans.c +++ b/src/vlog/vlog-trans.c @@ -101,7 +101,7 @@ static type_t trans_var_type(vlog_node_t decl) static type_t trans_net_type(vlog_node_t decl) { - return trans_type(decl, VERILOG_RESOLVED_NET, VERILOG_RESOLVED_PACKED_NET); + return trans_type(decl, VERILOG_RESOLVED_NET, VERILOG_RESOLVED_NET_ARRAY); } static void trans_port_decl(vlog_node_t decl, tree_t out) diff --git a/test/dump/vlog1.v b/test/dump/vlog1.v index 084d2f7b..e9a89a05 100644 --- a/test/dump/vlog1.v +++ b/test/dump/vlog1.v @@ -20,4 +20,5 @@ module mod2; assign bus = 3; pullup (supply1, supply0) p1 (w); mod u1 (w); + assign bus[2] = 4'b1; endmodule // mod2 diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index 8168f53b..d9732cb6 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -949,3 +949,4 @@ issue851 wave,2008 vlog8 verilog issue852 wave,2019,dump-arrays wave11 wave,2008,dump-arrays +vlog9 verilog diff --git a/test/regress/vlog9.v b/test/regress/vlog9.v new file mode 100644 index 00000000..94afb0f4 --- /dev/null +++ b/test/regress/vlog9.v @@ -0,0 +1,89 @@ +// +// Copyright (c) 2002 Steven Wilson (steve@ka6s.com) +// +// This source code is free software; you can redistribute it +// and/or modify it in source code form under the terms of the GNU +// General Public License as published by the Free Software +// Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA +// +// SDW: Synth of basic reg form +// +// +module basicreg ( clk, d, q); +input clk, d; +output [2:0] q; +reg [2:0] q; + +(* ivl_synthesis_on *) +always @(posedge clk) + begin + q[0] <= d; + q[1] <= d; + q[2] <= d; + end + +endmodule + +module vlog9 ; + +reg clk, d; + +wire [2:0] q; + +basicreg u_reg (clk,d,q); + +(* ivl_synthesis_off *) +initial + begin +// $dumpfile("test.vcd"); +// $dumpvars(0,test); + clk = 0; + d = 0; + # 1; + clk = 1; + # 1; + if (q !== 3'b0) + begin + $display("FAILED - Q isn't 0 on first edge"); + $finish; + end + d = 1; + # 1; + clk = 0; + # 1; + if (q !== 3'b0) + begin + $display("FAILED - Q isn't 0 after first falling edge"); + $finish; + end + # 1; + clk = 1; + # 1; + if (q !== 3'b111) + begin + #1 ; + $display("FAILED - Q isn't 1 2nd raising edge"); + $finish; + end + # 1; + clk = 0; + # 1; + if (q !== 3'b111) + begin + $display("FAILED - Q isn't 1 after 2nd falling edge"); + $finish; + end + $display("PASSED"); + + end +endmodule diff --git a/test/test_dump.c b/test/test_dump.c index bc4af034..9bdf8ea2 100644 --- a/test/test_dump.c +++ b/test/test_dump.c @@ -489,6 +489,7 @@ START_TEST(test_vlog1) " assign bus = 32'd3;\n" " pullup (supply1,supply0) p1 (w);\n" " mod u1 (w);\n" + " assign bus[32'd2] = 4'd1;\n" "endmodule // mod2\n\n"); tb_rewind(tb); diff --git a/test/test_vlog.c b/test/test_vlog.c index 2e716709..49a43afd 100644 --- a/test/test_vlog.c +++ b/test/test_vlog.c @@ -136,13 +136,15 @@ START_TEST(test_ports) { 19, "duplicate declaration of x" }, { 22, "duplicate declaration of y" }, { 31, "'o3' cannot be assigned in a procedural block" }, + { 43, "inconsistent dimensions for 'y'" }, + { 44, "cannot reference net 'x' in constant expression" }, { -1, NULL } }; expect_errors(expect); input_from_file(TESTDIR "/vlog/ports.v"); - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 6; i++) { vlog_node_t m = vlog_parse(); fail_if(m == NULL); fail_unless(vlog_kind(m) == V_MODULE); diff --git a/test/vlog/ports.v b/test/vlog/ports.v index c3c6b313..9008bee8 100644 --- a/test/vlog/ports.v +++ b/test/vlog/ports.v @@ -1,5 +1,5 @@ module ports1 (x, y, z, y); // Error - input x; + input x; output reg y; output y; // Error output z; @@ -36,3 +36,10 @@ module ports5 (x, y, z); input x, y; // OK output reg z; endmodule // ports5 + +module ports6 (x, y, z); + input [7:0] x; // OK + output [3:0] y; // OK + wire [5:0] y; // Error + input [x:0] z; // Error +endmodule // ports6 -- 2.39.2