diff --git a/elab_expr.cc b/elab_expr.cc index c084b8029..6ee81e04f 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -3363,9 +3363,12 @@ unsigned PECastSize::test_width(Design*des, NetScope*scope, width_mode_t&) ivl_assert(*this, size_); ivl_assert(*this, base_); + expr_width_ = 0; + NetExpr*size_ex = elab_and_eval(des, scope, size_, -1, true); NetEConst*size_ce = dynamic_cast(size_ex); - expr_width_ = size_ce ? size_ce->value().as_ulong() : 0; + if (size_ce && !size_ce->value().is_negative()) + expr_width_ = size_ce->value().as_ulong(); delete size_ex; if (expr_width_ == 0) { cerr << get_fileline() << ": error: Cast size expression " diff --git a/elab_type.cc b/elab_type.cc index 2e5fbdf74..b611f655b 100644 --- a/elab_type.cc +++ b/elab_type.cc @@ -242,6 +242,13 @@ ivl_type_t struct_type_t::elaborate_type_raw(Design*des, NetScope*scope) const ; cur_name != curp->names->end() ; ++ cur_name) { decl_assignment_t*namep = *cur_name; + if (packed_flag && namep->expr) { + cerr << namep->expr->get_fileline() << " error: " + << "Packed structs must not have default member values." + << endl; + des->errors++; + } + netstruct_t::member_t memb; memb.name = namep->name; memb.net_type = elaborate_array_type(des, scope, *this, diff --git a/ivlpp/lexor.lex b/ivlpp/lexor.lex index bc34a1fac..c4987af7a 100644 --- a/ivlpp/lexor.lex +++ b/ivlpp/lexor.lex @@ -1130,12 +1130,14 @@ void free_macros(void) * variables. When the define is over, the def_finish() function * executes the define and clears this text. The define_continue_flag * is set if do_define detects that the definition is to be continued - * on the next line. + * on the next line. The define_comment_flag is set when a multi-line comment is + * active in a define. */ static char* define_text = 0; static size_t define_cnt = 0; static int define_continue_flag = 0; +static int define_comment_flag = 0; /* * The do_magic function puts the expansions of magic macros into @@ -1189,6 +1191,33 @@ static char *find_arg(char*ptr, char*head, char*arg) return cp; } +/* + * Returns 1 if the comment continues on the next line + */ +static int do_define_multiline_comment(char *replace_start, + const char *search_start) +{ + char *tail = strstr(search_start, "*/"); + + if (!tail) { + if (search_start[strlen(search_start) - 1] == '\\') { + define_continue_flag = 1; + define_comment_flag = 1; + *replace_start++ = '\\'; + } else { + define_comment_flag = 0; + fprintf(stderr, "%s:%u: Unterminated comment in define\n", + istack->path, istack->lineno+1); + } + *replace_start = '\0'; + return 1; + } + define_comment_flag = 0; + tail += 2; + memmove(replace_start, tail, strlen(tail) + 1); + return 0; +} + /* * Collect the definition. Normally, this returns 0. If there is a * continuation, then return 1 and this function may be called again @@ -1204,6 +1233,12 @@ static void do_define(void) define_continue_flag = 0; + /* Are we in an multi-line comment? Look for the end */ + if (define_comment_flag) { + if (do_define_multiline_comment(yytext, yytext)) + return; + } + /* Look for comments in the definition, and remove them. */ cp = strchr(yytext, '/'); @@ -1215,24 +1250,14 @@ static void do_define(void) } *cp = 0; break; - } - - if (cp[1] == '*') { - tail = strstr(cp+2, "*/"); - - if (tail == 0) { - *cp = 0; - fprintf(stderr, "%s:%u: Unterminated comment in define\n", - istack->path, istack->lineno+1 - ); + } else if (cp[1] == '*') { + if (do_define_multiline_comment(cp, cp + 2)) break; - } + } else { + cp++; + } - memmove(cp, tail+2, strlen(tail+2)+1); - continue; - } - - cp = strchr(cp+1, '/'); + cp = strchr(cp, '/'); } /* Trim trailing white space. */ diff --git a/ivtest/ivltests/always_comb_void_func.v b/ivtest/ivltests/always_comb_void_func.v new file mode 100644 index 000000000..0272393b9 --- /dev/null +++ b/ivtest/ivltests/always_comb_void_func.v @@ -0,0 +1,51 @@ +// Check that variables referenced in a void function contribute to the +// sensitivity list of a always_comb block. + +module top; + logic passed; + logic [7:0] value; + integer counter; + + function automatic void count(bit what); + counter = 0; + for (integer i = 0; i < $bits(value); i++) begin + if (value[i] == what) + counter += 1; + end + endfunction + + always_comb begin + count(1'b1); + end + + initial begin + passed = 1'b1; + + value = 8'b0000_0000; + #1; + if (counter !== 0) begin + $display("Expected 0, got %d", counter); + passed = 1'b0; + end + + value = 8'b0011_1100; + #1; + if (counter !== 4) begin + $display("Expected 4, got %d", counter); + passed = 1'b0; + end + + value = 8'b1011_1101; + #1; + if (counter !== 6) begin + $display("Expected 6, got %d", counter); + passed = 1'b0; + end + + if (passed) begin + $display("PASSED"); + end else begin + $display("FAILED"); + end + end +endmodule diff --git a/ivtest/ivltests/macro_comment1.v b/ivtest/ivltests/macro_comment1.v new file mode 100644 index 000000000..c1004fb61 --- /dev/null +++ b/ivtest/ivltests/macro_comment1.v @@ -0,0 +1,16 @@ +// Check that a '*' or '/' following a C-style comment is supported in a macro + +`define x(a, b) a /* comment */ * b +`define y(a, b) a /* comment */ / b + + module test; + + initial begin + if (`x(2, 3) === 6 && `y(8, 2) === 4) begin + $display("PASSED"); + end else begin + $display("FAILED"); + end + end + + endmodule diff --git a/ivtest/ivltests/macro_comment2.v b/ivtest/ivltests/macro_comment2.v new file mode 100644 index 000000000..3908da21b --- /dev/null +++ b/ivtest/ivltests/macro_comment2.v @@ -0,0 +1,17 @@ +// Check that another comment directly following a C-style comment is +// supported in a macro. + +`define x(a, b) a /* comment */// * b +`define y(a, b) a /* comment *//*/ b*/ + + module test; + + initial begin + if (`x(2, 3) === 2 && `y(8, 2) === 8) begin + $display("PASSED"); + end else begin + $display("FAILED"); + end + end + + endmodule diff --git a/ivtest/ivltests/macro_comment3.v b/ivtest/ivltests/macro_comment3.v new file mode 100644 index 000000000..422d0d97f --- /dev/null +++ b/ivtest/ivltests/macro_comment3.v @@ -0,0 +1,16 @@ +// Check that a '/' directly after opening a C-style comment gets handled +// correctly in a macro. + +`define x(a, b) a /*/ b */ + b + + module test; + + initial begin + if (`x(2, 3) === 5) begin + $display("PASSED"); + end else begin + $display("FAILED"); + end + end + + endmodule diff --git a/ivtest/ivltests/macro_comment_multiline.v b/ivtest/ivltests/macro_comment_multiline.v new file mode 100644 index 000000000..3a7c4b18a --- /dev/null +++ b/ivtest/ivltests/macro_comment_multiline.v @@ -0,0 +1,23 @@ +// Check that multi-line comments in macros are supported. Including some corner +// cases like another comment start in the comment. + +`define test(a, b, c) \ + (a + /* these is some \ + multi line */ b /* comment \ + that goes on \ + and on */ ) /* and some more \ +\ + // and even has a comments \ + /* in the comment */ * c + +module test; + + initial begin + if (`test(1, 2, 3) === 9) begin + $display("PASSED"); + end else begin + $display("FAILED"); + end + end + +endmodule diff --git a/ivtest/ivltests/size_cast_fail1.v b/ivtest/ivltests/size_cast_fail1.v new file mode 100644 index 000000000..dbd520355 --- /dev/null +++ b/ivtest/ivltests/size_cast_fail1.v @@ -0,0 +1,12 @@ +// Check that an error is reported when using a zero value for a size cast. + +module test; + + localparam integer N = 0; + + initial begin + int x, y; + y = N'(x); // This should fail, N is zero + end + +endmodule diff --git a/ivtest/ivltests/size_cast_fail2.v b/ivtest/ivltests/size_cast_fail2.v new file mode 100644 index 000000000..c7a23ad87 --- /dev/null +++ b/ivtest/ivltests/size_cast_fail2.v @@ -0,0 +1,12 @@ +// Check that an error is reported when using a negative value for a size cast. + +module test; + + localparam integer N = -1; + + initial begin + int x, y; + y = N'(x); // This should fail, N is negative + end + +endmodule diff --git a/ivtest/ivltests/size_cast_fail3.v b/ivtest/ivltests/size_cast_fail3.v new file mode 100644 index 000000000..986c05281 --- /dev/null +++ b/ivtest/ivltests/size_cast_fail3.v @@ -0,0 +1,12 @@ +// Check that an error is reported when using an undefined value for a size cast. + +module test; + + localparam integer N = 32'hx; + + initial begin + int x, y; + y = N'(x); // This should fail, N is undefined + end + +endmodule diff --git a/ivtest/ivltests/struct_packed_member_def.v b/ivtest/ivltests/struct_packed_member_def.v new file mode 100644 index 000000000..1e9b58129 --- /dev/null +++ b/ivtest/ivltests/struct_packed_member_def.v @@ -0,0 +1,13 @@ +// Check that an error is reported when specifing a default member value for a +// packed struct. + +module test; + struct packed { + // This should fail, default member value is not allowed for packed struct + integer x = 10; + } s; + + initial begin + $display("FAILED"); + end +endmodule diff --git a/ivtest/regress-sv.list b/ivtest/regress-sv.list index be291eb87..2e5fcc75e 100644 --- a/ivtest/regress-sv.list +++ b/ivtest/regress-sv.list @@ -76,6 +76,7 @@ always_comb_fail4 CE,-g2005-sv ivltests always_comb_no_sens nornal,-g2005-sv ivltests gold=always_comb_no_sens.gold always_comb_rfunc nornal,-g2005-sv ivltests always_comb_trig normal,-g2005-sv ivltests +always_comb_void_func normal,-g2005-sv ivltests always_comb_warn normal,-g2005-sv ivltests gold=always_comb_warn.gold always_ff normal,-g2005-sv ivltests always_ff_fail CE,-g2005-sv ivltests @@ -465,6 +466,9 @@ size_cast2 normal,-g2005-sv ivltests size_cast3 normal,-g2009 ivltests size_cast4 normal,-g2009 ivltests size_cast5 normal,-g2009 ivltests +size_cast_fail1 CE,-g2009 ivltests +size_cast_fail2 CE,-g2009 ivltests +size_cast_fail3 CE,-g2009 ivltests slongint_test normal,-g2005-sv ivltests sshortint_test normal,-g2005-sv ivltests string_events normal,-g2009 ivltests gold=string_events.gold @@ -485,6 +489,7 @@ struct_member_signed normal,-g2009 ivltests struct_packed_array normal,-g2009 ivltests struct_packed_array2 normal,-g2009 ivltests struct_packed_darray_fail CE,-g2009 ivltests +struct_packed_member_def CE,-g2009 ivltests struct_packed_queue_fail CE,-g2009 ivltests struct_packed_sysfunct normal,-g2009 ivltests struct_packed_sysfunct2 normal,-g2009 ivltests diff --git a/ivtest/regress-vlg.list b/ivtest/regress-vlg.list index ef165b190..977970c83 100644 --- a/ivtest/regress-vlg.list +++ b/ivtest/regress-vlg.list @@ -635,6 +635,10 @@ localparam_type normal ivltests gold=parameter_type.gold long_div normal ivltests gold=long_div.gold macro2 normal ivltests macro_args CO,-yivltests ivltests +macro_comment1 normal ivltests +macro_comment2 normal ivltests +macro_comment3 normal ivltests +macro_comment_multiline normal ivltests macro_redefinition normal,-Wmacro-redefinition ivltests gold=macro_redefinition.gold macro_replacement normal,-Wmacro-replacement ivltests gold=macro_replacement.gold macsub normal ivltests diff --git a/ivtest/regress-vlog95.list b/ivtest/regress-vlog95.list index aaf99d53f..6475f29af 100644 --- a/ivtest/regress-vlog95.list +++ b/ivtest/regress-vlog95.list @@ -69,6 +69,7 @@ # Verilog 95 does not support automatic tasks or functions. always_comb_rfunc CE ivltests +always_comb_void_func CE ivltests automatic_error11 CE ivltests automatic_error12 CE ivltests automatic_error13 CE ivltests diff --git a/net_nex_input.cc b/net_nex_input.cc index 7d68229ef..8c45056c8 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -255,6 +255,30 @@ NexusSet* NetETernary::nex_input(bool rem_out, bool always_sens, bool nested_fun return result; } +// Get the contribution of a function call in a always_comb block +static void func_always_sens(NetFuncDef *func, NexusSet *result, + bool rem_out, bool nested_func) +{ + // Avoid recursive function calls. + static set func_set; + if (!nested_func) + func_set.clear(); + + if (!func_set.insert(func).second) + return; + + std::unique_ptr tmp(func->proc()->nex_input(rem_out, true, true)); + // Remove the function inputs + std::unique_ptr in(new NexusSet); + for (unsigned idx = 0; idx < func->port_count(); idx++) { + NetNet *net = func->port(idx); + assert(net->pin_count() == 1); + in->add(net->pin(0).nexus(), 0, net->vector_width()); + } + tmp->rem(*in); + result->add(*tmp); +} + NexusSet* NetEUFunc::nex_input(bool rem_out, bool always_sens, bool nested_func) const { NexusSet*result = new NexusSet; @@ -265,31 +289,8 @@ NexusSet* NetEUFunc::nex_input(bool rem_out, bool always_sens, bool nested_func) delete tmp; } - if (always_sens) { - NetFuncDef*func = func_->func_def(); - - // Avoid recursive function calls. - static set func_set; - if (!nested_func) - func_set.clear(); - - if (!func_set.insert(func).second) - return result; - - NexusSet*tmp = func->proc()->nex_input(rem_out, always_sens, true); - // Remove the function inputs - NexusSet*in = new NexusSet; - for (unsigned idx = 0 ; idx < func->port_count() ; idx += 1) { - NetNet*net = func->port(idx); - assert(net->pin_count() == 1); - in->add(net->pin(0).nexus(), 0, net->vector_width()); - } - tmp->rem(*in); - delete in; - - result->add(*tmp); - delete tmp; - } + if (always_sens) + func_always_sens(func_->func_def(), result, rem_out, nested_func); return result; } @@ -613,9 +614,18 @@ NexusSet* NetSTask::nex_input(bool rem_out, bool always_sens, bool nested_func) * parameters to consider, because the compiler already removed them * and converted them to blocking assignments. */ -NexusSet* NetUTask::nex_input(bool, bool, bool) const +NexusSet* NetUTask::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - return new NexusSet; + NexusSet *result = new NexusSet; + + /* + * Let the contents of void functions contribute to the sensitivity list + * of always_comb blocks + */ + if (always_sens && task_->type() == NetScope::FUNC) + func_always_sens(task_->func_def(), result, rem_out, nested_func); + + return result; } NexusSet* NetWhile::nex_input(bool rem_out, bool always_sens, bool nested_func) const