diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 9817e4f57..46f2b0f0b 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -969,6 +969,35 @@ class WidthVisitor final : public VNVisitor { nodep->dtypeSetInteger(); } } + bool streamImplicitUseAllowed(const AstNodeStream* nodep) const { + const AstNode* backp = nodep->backp(); + while (true) { + if (const AstArg* const argp = VN_CAST(backp, Arg)) { + backp = argp->backp(); + } else if (const AstSFormatArg* const argp = VN_CAST(backp, SFormatArg)) { + backp = argp->backp(); + } else if (VN_IS(backp, CvtArrayToArray) || VN_IS(backp, CvtArrayToPacked) + || VN_IS(backp, CvtPackedToArray) || VN_IS(backp, CvtPackString) + || VN_IS(backp, CvtUnpackedToQueue)) { + backp = backp->backp(); + } else { + break; + } + } + if (!backp) return true; // Error elsewhere + + // IEEE allows streaming concatenations as assignment sources/targets, nested inside + // another stream, or after an explicit cast. Other expression operands require a cast. + return VN_IS(backp, NodeAssign) // + || VN_IS(backp, Var) // + || VN_IS(backp, MemberDType) // + || VN_IS(backp, Pin) // + || VN_IS(backp, NodeFTaskRef) // + || VN_IS(backp, NodeStream) // + || VN_IS(backp, Cast) // + || VN_IS(backp, CastSize) // + || VN_IS(backp, CastWrap); + } void visit(AstNodeStream* nodep) override { VL_RESTORER(m_streamConcat); // UINFOTREE(1, nodep, "stream-in vup" << m_vup, "stream-in "); @@ -1002,9 +1031,7 @@ class WidthVisitor final : public VNVisitor { } } if (m_vup->final()) { - const AstNode* backp = nodep->backp(); - if (VN_IS(backp, SFormatArg)) backp = backp->backp(); - if (VN_IS(backp, SFormatF)) { + if (!streamImplicitUseAllowed(nodep)) { nodep->v3error( "Streaming concatenation cannot be used in an implicitly cast context " "(IEEE 1800-2023 11.4.17)\n" diff --git a/test_regress/t/t_display_stream_bad.out b/test_regress/t/t_display_stream_bad.out index 0eb1912e5..823853243 100644 --- a/test_regress/t/t_display_stream_bad.out +++ b/test_regress/t/t_display_stream_bad.out @@ -1,17 +1,42 @@ -%Error: t/t_display_stream_bad.v:11:15: Streaming concatenation cannot be used in an implicitly cast context (IEEE 1800-2023 11.4.17) +%Error: t/t_display_stream_bad.v:14:15: Streaming concatenation cannot be used in an implicitly cast context (IEEE 1800-2023 11.4.17) : ... note: In instance 't' : ... Suggest use a cast - 11 | $display({<<{value}}); + 14 | result = {<<{value}} + 1; | ^~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_display_stream_bad.v:12:22: Streaming concatenation cannot be used in an implicitly cast context (IEEE 1800-2023 11.4.17) +%Error: t/t_display_stream_bad.v:15:23: Streaming concatenation cannot be used in an implicitly cast context (IEEE 1800-2023 11.4.17) : ... note: In instance 't' : ... Suggest use a cast - 12 | $display("%0d", {<<{value}}); + 15 | result = value + {<<{value}}; + | ^~ +%Error: t/t_display_stream_bad.v:16:26: Streaming concatenation cannot be used in an implicitly cast context (IEEE 1800-2023 11.4.17) + : ... note: In instance 't' + : ... Suggest use a cast + 16 | result = value[0] ? {<<{value}} : other; + | ^~ +%Error: t/t_display_stream_bad.v:17:16: Streaming concatenation cannot be used in an implicitly cast context (IEEE 1800-2023 11.4.17) + : ... note: In instance 't' + : ... Suggest use a cast + 17 | result = {{<<{value}}}; + | ^~ +%Error: t/t_display_stream_bad.v:18:14: Streaming concatenation cannot be used in an implicitly cast context (IEEE 1800-2023 11.4.17) + : ... note: In instance 't' + : ... Suggest use a cast + 18 | flag = ({<<{value}} == other); + | ^~ +%Error: t/t_display_stream_bad.v:19:15: Streaming concatenation cannot be used in an implicitly cast context (IEEE 1800-2023 11.4.17) + : ... note: In instance 't' + : ... Suggest use a cast + 19 | $display({<<{value}}); + | ^~ +%Error: t/t_display_stream_bad.v:20:22: Streaming concatenation cannot be used in an implicitly cast context (IEEE 1800-2023 11.4.17) + : ... note: In instance 't' + : ... Suggest use a cast + 20 | $display("%0d", {<<{value}}); | ^~ -%Error: t/t_display_stream_bad.v:13:29: Streaming concatenation cannot be used in an implicitly cast context (IEEE 1800-2023 11.4.17) +%Error: t/t_display_stream_bad.v:21:29: Streaming concatenation cannot be used in an implicitly cast context (IEEE 1800-2023 11.4.17) : ... note: In instance 't' : ... Suggest use a cast - 13 | void'($sformatf("%0d", {<<{value}})); + 21 | void'($sformatf("%0d", {<<{value}})); | ^~ %Error: Exiting due to diff --git a/test_regress/t/t_display_stream_bad.v b/test_regress/t/t_display_stream_bad.v index 3ff9e0472..9c534a277 100644 --- a/test_regress/t/t_display_stream_bad.v +++ b/test_regress/t/t_display_stream_bad.v @@ -6,10 +6,19 @@ module t; int value = 1; + int other = 2; + int result; + bit flag; initial begin + result = {<<{value}} + 1; + result = value + {<<{value}}; + result = value[0] ? {<<{value}} : other; + result = {{<<{value}}}; + flag = ({<<{value}} == other); $display({<<{value}}); $display("%0d", {<<{value}}); void'($sformatf("%0d", {<<{value}})); + result = int'({<<{value}}); end endmodule diff --git a/test_regress/t/t_stream_queue.v b/test_regress/t/t_stream_queue.v index 7e6b333b9..68aecda1e 100644 --- a/test_regress/t/t_stream_queue.v +++ b/test_regress/t/t_stream_queue.v @@ -5,7 +5,17 @@ // SPDX-License-Identifier: CC0-1.0 `define stop $stop -`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%h' exp='%h'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +`define checks_w(width,gotv,expv) do begin \ + logic [(width)-1:0] got_check; \ + logic [(width)-1:0] exp_check; \ + got_check = (gotv); \ + exp_check = (expv); \ + if (got_check != exp_check) begin \ + $write("%%Error: %s:%0d: got='%h' exp='%h'\n", `__FILE__,`__LINE__, got_check, exp_check); \ + `stop; \ + end \ +end while(0); +`define checks(gotv,expv) `checks_w($bits(expv), gotv, expv) module t; logic [7:0] i_char; @@ -69,8 +79,8 @@ module t; byte_pkt = {<<8{i_header,i_len,i_crc,i_data}}; {<<8{o_header,o_len,o_crc,o_data}} = byte_pkt; - `checks({>>{byte_pkt}},{<<8{i_header,i_len,i_crc,i_data}}); - `checks({i_header,i_len,i_crc,i_data},{<<8{byte_pkt}}); + `checks_w(128, {>>{byte_pkt}},{<<8{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {i_header,i_len,i_crc,i_data},{<<8{byte_pkt}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); // //----------- SData QUEUE -------- @@ -95,7 +105,7 @@ module t; sdata_pkt = {<<8{i_header,i_len,i_crc,i_data}}; {<<8{o_header,o_len,o_crc,o_data}} = sdata_pkt; - `checks({>>{sdata_pkt}},{<<8{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{sdata_pkt}},{<<8{i_header,i_len,i_crc,i_data}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); //----------- IData QUEUE -------- @@ -111,7 +121,7 @@ module t; int_pkt = {<<8{i_header,i_len,i_crc,i_data}}; {<<8{o_header,o_len,o_crc,o_data}} = int_pkt; - `checks({>>{int_pkt}},{<<8{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{int_pkt}},{<<8{i_header,i_len,i_crc,i_data}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); //----------- QData QUEUE -------- @@ -130,7 +140,7 @@ module t; qdata_pkt = {<<8{i_header,i_len,i_crc,i_data}}; {<<8{o_header,o_len,o_crc,o_data}} = qdata_pkt; - `checks({>>{qdata_pkt}},{<<8{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{qdata_pkt}},{<<8{i_header,i_len,i_crc,i_data}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); // ----------- VLWide QUEUE -------- @@ -143,7 +153,7 @@ module t; /* verilator lint_off WIDTHEXPAND */ wide129 = {<<8{i_header,i_len,i_crc,i_data}}; - `checks({>>{vlwide_pkt_129}},wide129); + `checks_w(129, {>>{vlwide_pkt_129}},wide129); /* verilator lint_on WIDTHEXPAND */ //------------------------------- REVERSE ENDIAN ------------------------------ @@ -167,8 +177,8 @@ module t; byte_pkt_rev = {<<8{i_header,i_len,i_crc,i_data}}; {<<8{o_header,o_len,o_crc,o_data}} = byte_pkt_rev; - `checks({>>{byte_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}}); - `checks({i_header,i_len,i_crc,i_data},{<<8{byte_pkt_rev}}); + `checks_w(128, {>>{byte_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {i_header,i_len,i_crc,i_data},{<<8{byte_pkt_rev}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); //----------- SData QUEUE -------- @@ -188,7 +198,7 @@ module t; sdata_pkt_rev = {<<8{i_header,i_len,i_crc,i_data}}; {<<8{o_header,o_len,o_crc,o_data}} = sdata_pkt_rev; - `checks({>>{sdata_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{sdata_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); //----------- IData QUEUE -------- @@ -204,7 +214,7 @@ module t; int_pkt_rev = {<<8{i_header,i_len,i_crc,i_data}}; {<<8{o_header,o_len,o_crc,o_data}} = int_pkt_rev; - `checks({>>{int_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{int_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); //----------- QData QUEUE -------- @@ -218,7 +228,7 @@ module t; qdata_pkt_rev = {<<8{i_header,i_len,i_crc,i_data}}; {<<8{o_header,o_len,o_crc,o_data}} = qdata_pkt_rev; - `checks({>>{qdata_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{qdata_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); // ----------- VLWide QUEUE -------- @@ -227,7 +237,7 @@ module t; /* verilator lint_off WIDTHEXPAND */ wide129 = {<<8{i_header,i_len,i_crc,i_data}}; /* verilator lint_on WIDTHEXPAND */ - `checks({>>{vlwide_pkt_129_rev}},wide129); + `checks_w(129, {>>{vlwide_pkt_129_rev}},wide129); // // -------------------- STREAMR ------------------------------------ // //----------- CData QUEUE -------- @@ -237,21 +247,21 @@ module t; byte_pkt = {>>{i_header,i_len}}; {>>{o_header,o_len}} = byte_pkt; - `checks({>>{i_header,i_len}},{>>{o_header,o_len}}); + `checks_w(64, {>>{i_header,i_len}},{>>{o_header,o_len}}); `checks({i_header,i_len},{o_header,o_len}); byte_pkt = {>>{i_header,i_len,i_crc,i_data}}; {>>{o_header,o_len,o_crc,o_data}} = byte_pkt; - `checks({>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); //----------- IData QUEUE -------- int_pkt = {>>{i_header}}; o_header = {>>{int_pkt}}; `checks(o_header,i_header); - `checks(o_header,{>>{int_pkt}}); - `checks({>>{o_header}},{>>{int_pkt}}); + `checks_w(32, o_header,{>>{int_pkt}}); + `checks_w(32, {>>{o_header}},{>>{int_pkt}}); //test with QData int_pkt = {>>{i_header,i_len}}; @@ -261,7 +271,7 @@ module t; int_pkt = {>>{i_header,i_len,i_crc,i_data}}; {>>{o_header,o_len,o_crc,o_data}} = int_pkt; - `checks({>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); //----------- QData QUEUE -------- @@ -274,7 +284,7 @@ module t; qdata_pkt = {>>{i_header,i_len,i_crc,i_data}}; {>>{o_header,o_len,o_crc,o_data}} = qdata_pkt; - `checks({>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); // ----------- VLWide QUEUE -------- @@ -288,84 +298,84 @@ module t; vlwide_pkt_129 = {>>{i_header,i_len,i_crc,i_data}}; {>>{o_header,o_len,o_crc,o_data}} = vlwide_pkt_129; - `checks({>>{vlwide_pkt_129}},{>>{1'b0,i_header,i_len,i_crc,i_data}}); + `checks_w(129, {>>{vlwide_pkt_129}},{>>{1'b0,i_header,i_len,i_crc,i_data}}); `checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data}); //---------- into other queues ------ int_pkt = {>>{i_header,i_len,i_crc,i_data}}; byte_pkt = {>>{int_pkt}}; - `checks({>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}}); byte_pkt = {>>{i_header,i_len,i_crc,i_data}}; int_pkt = {>>{byte_pkt}}; - `checks({>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}}); byte_pkt = {>>{i_header,i_len,i_crc,i_data}}; int_pkt = {>>{byte_pkt}}; - `checks({>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}}); sdata_pkt = {>>{i_header,i_len,i_crc,i_data}}; byte_pkt = {>>{sdata_pkt}}; - `checks({>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}}); byte_pkt = {>>{i_header,i_len,i_crc,i_data}}; sdata_pkt = {>>{byte_pkt}}; - `checks({>>{sdata_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{sdata_pkt}},{>>{i_header,i_len,i_crc,i_data}}); byte_pkt = {>>{i_header,i_len,i_crc,i_data}}; qdata_pkt = {>>{byte_pkt}}; - `checks({>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}}); qdata_pkt = {>>{i_header,i_len,i_crc,i_data}}; byte_pkt = {>>{qdata_pkt}}; - `checks({>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}}); qdata_pkt = {>>{i_header,i_len,i_crc,i_data}}; int_pkt = {>>{qdata_pkt}}; - `checks({>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}}); int_pkt = {>>{i_header,i_len,i_crc,i_data}}; qdata_pkt = {>>{int_pkt}}; - `checks({>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}}); byte_pkt = {>>{i_header,i_len,i_crc,i_data}}; vlwide_pkt_128 = {>>{byte_pkt}}; - `checks({>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data}}); vlwide_pkt_128 = {>>{i_header,i_len,i_crc,i_data}}; byte_pkt = {>>{vlwide_pkt_128}}; - `checks({i_header,i_len,i_crc,i_data},{>>{byte_pkt}}); - `checks({>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {i_header,i_len,i_crc,i_data},{>>{byte_pkt}}); + `checks_w(128, {>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}}); int_pkt = {>>{i_header,i_len,i_crc,i_data}}; vlwide_pkt_128 = {>>{int_pkt}}; - `checks({i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}}); - `checks({>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}}); + `checks_w(128, {>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data}}); vlwide_pkt_128 = {>>{i_header,i_len,i_crc,i_data}}; int_pkt = {>>{vlwide_pkt_128}}; - `checks({i_header,i_len,i_crc,i_data},{>>{int_pkt}}); - `checks({>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {i_header,i_len,i_crc,i_data},{>>{int_pkt}}); + `checks_w(128, {>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}}); qdata_pkt = {>>{i_header,i_len,i_crc,i_data}}; vlwide_pkt_128 = {>>{qdata_pkt}}; - `checks({i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}}); - `checks({>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}}); + `checks_w(128, {>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data}}); qdata_pkt = {>>{i_header,i_len,i_crc,i_data,i_header,i_len,i_crc,i_data}}; vlwide_pkt_128 = {>>{qdata_pkt}}; - `checks({i_header,i_len,i_crc,i_data,i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}}); - `checks({>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data,i_header,i_len,i_crc,i_data}}); + `checks_w(256, {i_header,i_len,i_crc,i_data,i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}}); + `checks_w(256, {>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data,i_header,i_len,i_crc,i_data}}); qdata_pkt = {>>{i_header,i_len,i_crc,i_data,i_header,i_len,i_crc}}; vlwide_pkt_128 = {>>{qdata_pkt}}; - `checks({32'h0,i_header,i_len,i_crc,i_data,i_header,i_len,i_crc},{>>{vlwide_pkt_128}}); - `checks({>>{vlwide_pkt_128}},{>>{32'h0,i_header,i_len,i_crc,i_data,i_header,i_len,i_crc}}); + `checks_w(256, {32'h0,i_header,i_len,i_crc,i_data,i_header,i_len,i_crc},{>>{vlwide_pkt_128}}); + `checks_w(256, {>>{vlwide_pkt_128}},{>>{32'h0,i_header,i_len,i_crc,i_data,i_header,i_len,i_crc}}); vlwide_pkt_128 = {>>{i_header,i_len,i_crc,i_data}}; qdata_pkt = {>>{vlwide_pkt_128}}; - `checks({i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}}); - `checks({>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}}); + `checks_w(128, {i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}}); + `checks_w(128, {>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}}); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_stream_queue_interface.sv b/test_regress/t/t_stream_queue_interface.sv index 290a174d7..f043f8e6d 100644 --- a/test_regress/t/t_stream_queue_interface.sv +++ b/test_regress/t/t_stream_queue_interface.sv @@ -21,15 +21,17 @@ module t; pkt_if pkt_if_init (clk); //this will not compile without -fno-life initial begin - byte byte_pkt[$]; + byte byte_pkt[$]; + byte got_pkt; //---------------------- STREAM WITH INTERFACE ------------------- //using this forces verilator to a AstSel Node into a Stream Node #0 //make sure we dont optimize it all away in v3life pkt_if_init.s.extra = 8'hd; byte_pkt = {>>{pkt_if_init.s.extra}}; - if(8'hd == {>>{byte_pkt}}) begin - $write("*-* All Finished *-*\n"); - $finish(); + got_pkt = {>>{byte_pkt}}; + if (8'hd == got_pkt) begin + $write("*-* All Finished *-*\n"); + $finish(); end end