Broaden streaming concat context checks

This commit is contained in:
JAEUK LEE 2026-07-08 13:33:39 +09:00
parent 6303bc454a
commit 087d5c7280
5 changed files with 130 additions and 57 deletions

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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