fix(interface): allow forward interface port types

This commit is contained in:
Jose Tejada 2026-05-11 22:25:32 +02:00
parent 377881b723
commit 56afcb6e75
18 changed files with 137 additions and 54 deletions

View File

@ -1,3 +1,3 @@
ivltests/sv_interface_port_forwarding_restrict_fail.v:30: error: Interface member `hidden' is not listed in modport `consumer'.
ivltests/sv_interface_port_forwarding_restrict_fail.v:30: error: Unable to elaborate r-value: bus.hidden
ivltests/sv_interface_port_forwarding_restrict_fail.v:23: error: Interface member `hidden' is not listed in modport `consumer'.
ivltests/sv_interface_port_forwarding_restrict_fail.v:23: error: Unable to elaborate r-value: bus.hidden
2 error(s) during elaboration.

View File

@ -0,0 +1,2 @@
ivltests/sv_interface_port_missing_type_fail.v:7: error: Interface port bus uses unknown interface type `missing_if'.
1 error(s) during elaboration.

View File

@ -1,2 +1,2 @@
ivltests/sv_interface_port_positional_unconnected_fail.v:13: error: Interface port `bus' of module bus_user is not connected.
ivltests/sv_interface_port_positional_unconnected_fail.v:7: error: Interface port `bus' of module bus_user is not connected.
Elaboration failed

View File

@ -0,0 +1,2 @@
ivltests/sv_interface_port_unmodported_missing_type_fail.v:7: error: Interface port bus uses unknown interface type `missing_if'.
1 error(s) during elaboration.

View File

@ -4,14 +4,6 @@
// This file is placed into the Public Domain, for any use, without
// warranty.
interface bus_if ();
logic value;
logic mirror;
logic hidden;
modport consumer(input value, output mirror);
endinterface
module test;
bus_if bus();
@ -39,3 +31,11 @@ module child(
);
assign bus.mirror = bus.value;
endmodule
interface bus_if ();
logic value;
logic mirror;
logic hidden;
modport consumer(input value, output mirror);
endinterface

View File

@ -4,13 +4,6 @@
// This file is placed into the Public Domain, for any use, without
// warranty.
interface bus_if ();
logic value;
logic hidden;
modport consumer(input value);
endinterface
module test;
bus_if bus();
parent dut(.bus(bus));
@ -29,3 +22,10 @@ module child(
assign sample = bus.hidden;
endmodule
interface bus_if ();
logic value;
logic hidden;
modport consumer(input value);
endinterface

View File

@ -0,0 +1,11 @@
// This tests the diagnostic path for an interface-typed module port
// whose interface type name is not declared.
//
// This file is placed into the Public Domain, for any use, without
// warranty.
module bus_user(
missing_if.consumer bus
);
initial $display("FAILED");
endmodule

View File

@ -3,13 +3,6 @@
// This file is placed into the Public Domain, for any use, without
// warranty.
interface bus_if ();
logic value;
logic sample;
modport consumer(input value, output sample);
endinterface
module test;
bus_if bus();
@ -31,3 +24,10 @@ module bus_user(
);
assign bus.sample = bus.value;
endmodule
interface bus_if ();
logic value;
logic sample;
modport consumer(input value, output sample);
endinterface

View File

@ -3,12 +3,6 @@
// This file is placed into the Public Domain, for any use, without
// warranty.
interface bus_if ();
logic value;
modport consumer(input value);
endinterface
module test;
bus_user dut();
endmodule
@ -17,3 +11,9 @@ module bus_user(
bus_if.consumer bus
);
endmodule
interface bus_if ();
logic value;
modport consumer(input value);
endinterface

View File

@ -4,12 +4,6 @@
// This file is placed into the Public Domain, for any use, without
// warranty.
interface bus_if ();
logic [7:0] lhs;
logic [7:0] rhs;
logic [8:0] sum;
endinterface
module test;
logic [7:0] lhs;
logic [7:0] rhs;
@ -38,3 +32,9 @@ module add_if(
);
assign bus.sum = bus.lhs + bus.rhs;
endmodule
interface bus_if ();
logic [7:0] lhs;
logic [7:0] rhs;
logic [8:0] sum;
endinterface

View File

@ -0,0 +1,11 @@
// This tests the diagnostic path for an unmodported interface-typed
// module port whose interface type name is not declared.
//
// This file is placed into the Public Domain, for any use, without
// warranty.
module bus_user(
missing_if bus
);
initial $display("FAILED");
endmodule

View File

@ -3,13 +3,6 @@
// This file is placed into the Public Domain, for any use, without
// warranty.
interface bus_if ();
logic value;
logic sample;
modport consumer(input value, output sample);
endinterface
module test;
bus_if bus();
@ -31,3 +24,10 @@ module bus_user(
);
assign bus.sample = bus.value;
endmodule
interface bus_if ();
logic value;
logic sample;
modport consumer(input value, output sample);
endinterface

View File

@ -270,12 +270,14 @@ sv_foreach9 vvp_tests/sv_foreach9.json
sv_foreach10 vvp_tests/sv_foreach10.json
sv_interface vvp_tests/sv_interface.json
sv_interface_port_basic vvp_tests/sv_interface_port_basic.json
sv_interface_port_missing_type_fail vvp_tests/sv_interface_port_missing_type_fail.json
sv_interface_port_missing_modport_fail vvp_tests/sv_interface_port_missing_modport_fail.json
sv_interface_port_non_interface_actual_fail vvp_tests/sv_interface_port_non_interface_actual_fail.json
sv_interface_port_wrong_type_fail vvp_tests/sv_interface_port_wrong_type_fail.json
sv_interface_port_modport_input_write_fail vvp_tests/sv_interface_port_modport_input_write_fail.json
sv_interface_port_unlisted_member_fail vvp_tests/sv_interface_port_unlisted_member_fail.json
sv_interface_port_unmodported_basic vvp_tests/sv_interface_port_unmodported_basic.json
sv_interface_port_unmodported_missing_type_fail vvp_tests/sv_interface_port_unmodported_missing_type_fail.json
sv_interface_port_forwarding vvp_tests/sv_interface_port_forwarding.json
sv_interface_port_forwarding_restrict_fail vvp_tests/sv_interface_port_forwarding_restrict_fail.json
sv_interface_port_positional vvp_tests/sv_interface_port_positional.json

View File

@ -0,0 +1,6 @@
{
"type" : "CE",
"source" : "sv_interface_port_missing_type_fail.v",
"gold" : "sv_interface_port_missing_type_fail",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,6 @@
{
"type" : "CE",
"source" : "sv_interface_port_unmodported_missing_type_fail.v",
"gold" : "sv_interface_port_unmodported_missing_type_fail",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -120,6 +120,8 @@ static list<int> keyword_mask_stack;
static int comment_enter;
static bool in_module = false;
static bool in_UDP = false;
static bool in_module_port_list = false;
static bool module_port_list_start = false;
bool in_celldefine = false;
UCDriveType uc_drive = UCD_NONE;
static int ts_state = 0;
@ -139,6 +141,12 @@ void lex_in_package_scope(PPackage*pkg)
in_package_scope = pkg;
}
void lex_in_module_port_list(bool flag)
{
in_module_port_list = flag;
module_port_list_start = flag;
}
%}
%x CCOMMENT
@ -160,6 +168,7 @@ void lex_in_package_scope(PPackage*pkg)
%x REAL_SCALE
W [ \t\b\f\r]+
ID [a-zA-Z_][a-zA-Z0-9$_]*
S [afpnumkKMGT]
@ -335,7 +344,7 @@ TU [munpf]
<EDGES>"z0" { return K_edge_descriptor; }
<EDGES>"z1" { return K_edge_descriptor; }
[a-zA-Z_][a-zA-Z0-9$_]* {
{ID} {
int rc = lexor_keyword_code(yytext, yyleng);
switch (rc) {
case IDENTIFIER:
@ -434,6 +443,22 @@ TU [munpf]
}
}
if (rc == IDENTIFIER && gn_system_verilog() &&
in_module_port_list && module_port_list_start) {
char save_ch = *yy_c_buf_p;
*yy_c_buf_p = yy_hold_char;
const char*cp = yy_c_buf_p;
while (*cp == ' ' || *cp == '\t' || *cp == '\b' ||
*cp == '\f' || *cp == '\r' || *cp == '\n')
cp += 1;
if (*cp == '.' || isalpha(static_cast<unsigned char>(*cp)) ||
*cp == '_' || *cp == '\\')
rc = INTERFACE_IDENTIFIER;
*yy_c_buf_p = save_ch;
}
if (in_module_port_list)
module_port_list_start = false;
return rc;
}
@ -906,7 +931,16 @@ TU [munpf]
`{W} { VLerror(yylloc, "error: Stray tic (`) here. Perhaps you put white "
"space between the tic and preprocessor directive?"); }
. { return yytext[0]; }
. {
if (in_module_port_list) {
if (yytext[0] == '(' || yytext[0] == ',')
module_port_list_start = true;
else if (yytext[0] != ')' && yytext[0] != '[' &&
yytext[0] != ']' && yytext[0] != ':')
module_port_list_start = false;
}
return yytext[0];
}
/* Final catchall. something got lost or mishandled. */
/* XXX Should we tell the user something about the lexical state? */

18
parse.y
View File

@ -4781,9 +4781,11 @@ module
port_declaration_context_init(); }
module_package_import_list_opt
module_parameter_port_list_opt
{ lex_in_module_port_list(true); }
module_port_list_opt
{ lex_in_module_port_list(false); }
module_attribute_foreign ';'
{ pform_module_set_ports($8); }
{ pform_module_set_ports($9); }
timeunits_declaration_opt
{ pform_set_scope_timescale(@2); }
module_item_list_opt
@ -4806,16 +4808,16 @@ module
}
// Check that program/endprogram and module/endmodule
// keywords match.
if ($2 != $15) {
if ($2 != $17) {
switch ($2) {
case K_module:
yyerror(@15, "error: module not closed by endmodule.");
yyerror(@17, "error: module not closed by endmodule.");
break;
case K_program:
yyerror(@15, "error: program not closed by endprogram.");
yyerror(@17, "error: program not closed by endprogram.");
break;
case K_interface:
yyerror(@15, "error: interface not closed by endinterface.");
yyerror(@17, "error: interface not closed by endinterface.");
break;
default:
break;
@ -4831,13 +4833,13 @@ module
// module.
switch ($2) {
case K_module:
check_end_label(@17, "module", $4, $17);
check_end_label(@19, "module", $4, $19);
break;
case K_program:
check_end_label(@17, "program", $4, $17);
check_end_label(@19, "program", $4, $19);
break;
case K_interface:
check_end_label(@17, "interface", $4, $17);
check_end_label(@19, "interface", $4, $19);
break;
default:
break;

View File

@ -83,6 +83,13 @@ extern UCDriveType uc_drive;
*/
extern void lex_in_package_scope(PPackage*pkg);
/*
* The parser signals when the lexor is scanning a module/interface/program
* port list so that ambiguous SystemVerilog interface formals can be
* tokenized without depending on declaration order.
*/
extern void lex_in_module_port_list(bool flag);
/*
* Test if this identifier is a type identifier in the current
* context. The pform code needs to help the lexor here because the