Fix false ASSIGNIN on interface input ports driven from outside (#7322)

This commit is contained in:
Yilou Wang 2026-03-26 17:30:16 +01:00 committed by GitHub
parent fbc3b3618d
commit e0963bd587
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 186 additions and 12 deletions

View File

@ -241,6 +241,7 @@ class WidthVisitor final : public VNVisitor {
std::map<const AstNode*, const AstClass*>
m_containingClassp; // Containing class cache for containingClass() function
std::unordered_set<AstVar*> m_aliasedVars; // Variables referenced in alias
std::unordered_set<const AstVar*> m_curModVars; // Variables declared in current module
static constexpr int ENUM_LOOKUP_BITS = 16; // Maximum # bits to make enum lookup table
@ -2835,7 +2836,18 @@ class WidthVisitor final : public VNVisitor {
&& (!m_ftaskp || !m_ftaskp->isConstructor())
&& !VN_IS(m_procedurep, InitialAutomatic) && !VN_IS(m_procedurep, InitialStatic)
&& !VN_IS(nodep->abovep(), AssignForce) && !VN_IS(nodep->abovep(), Release)) {
nodep->v3warn(ASSIGNIN, "Assigning to input/const variable: " << nodep->prettyNameQ());
// Skip ASSIGNIN for continuous assignments to net-type input ports
// via hierarchical reference. Net ports allow multiple continuous
// drivers (IEEE 1800-2023 23.3.3.3). Input ports default to net
// when port kind is omitted (23.2.2.3, PORT type).
const bool hierRef = !m_curModVars.count(nodep->varp());
const bool netPort
= nodep->varp()->isNet() || nodep->varp()->varType() == VVarType::PORT;
const bool contAssign = VN_IS(nodep->abovep(), AssignW);
if (!(hierRef && netPort && contAssign)) {
nodep->v3warn(ASSIGNIN,
"Assigning to input/const variable: " << nodep->prettyNameQ());
}
} else if (nodep->access().isWriteOrRW() && nodep->varp()->isConst() && !m_paramsOnly
&& (!m_ftaskp || !m_ftaskp->isConstructor())
&& !VN_IS(m_procedurep, InitialAutomatic)
@ -7374,6 +7386,9 @@ class WidthVisitor final : public VNVisitor {
} else {
VL_RESTORER(m_modep);
m_modep = nodep;
// Collect local variables for ASSIGNIN hierarchical reference check
m_curModVars.clear();
nodep->foreach([this](const AstVar* varp) { m_curModVars.insert(varp); });
userIterateChildren(nodep, nullptr);
}
}

View File

@ -1437,16 +1437,16 @@ port<nodep>: // ==IEEE: port
{ $$ = $3; VARDTYPE($2); VARIOANSI();
if (AstVar* vp = VARDONEP($$, $4, $5)) { addNextNull($$, vp); vp->valuep($7); } }
| portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE
{ $$ = $4; VARDTYPE($3); VARIOANSI();
{ $$ = $4; VARDECL(VAR); VARDTYPE($3); VARIOANSI();
addNextNull($$, VARDONEP($$, $5, $6)); }
| portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$ = $4; VARDTYPE($3); VARIOANSI();
{ $$ = $4; VARDECL(VAR); VARDTYPE($3); VARIOANSI();
if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } }
| portDirNetE yVAR implicit_typeE portSig variable_dimensionListE sigAttrListE
{ $$ = $4; VARDTYPE($3); VARIOANSI();
{ $$ = $4; VARDECL(VAR); VARDTYPE($3); VARIOANSI();
addNextNull($$, VARDONEP($$, $5, $6)); }
| portDirNetE yVAR implicit_typeE portSig variable_dimensionListE sigAttrListE '=' constExpr
{ $$ = $4; VARDTYPE($3); VARIOANSI();
{ $$ = $4; VARDECL(VAR); VARDTYPE($3); VARIOANSI();
if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } }
| portDirNetE signing portSig variable_dimensionListE sigAttrListE
{ $$ = $3;
@ -1955,10 +1955,10 @@ port_declaration<nodep>: // ==IEEE: port_declaration
/*mid*/ { VARDTYPE($3); }
/*cont*/ list_of_variable_decl_assignments { $$ = $5; }
| port_directionReset port_declNetE yVAR data_type
/*mid*/ { VARDTYPE($4); }
/*mid*/ { VARDECL(VAR); VARDTYPE($4); }
/*cont*/ list_of_variable_decl_assignments { $$ = $6; }
| port_directionReset port_declNetE yVAR implicit_typeE
/*mid*/ { VARDTYPE($4); }
/*mid*/ { VARDECL(VAR); VARDTYPE($4); }
/*cont*/ list_of_variable_decl_assignments { $$ = $6; }
| port_directionReset port_declNetE signingE rangeList
/*mid*/ { AstNodeDType* const dtp = GRAMMARP->addRange(

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(timing_loop=True, verilator_flags2=['--timing'])
test.execute()
test.passes()

View File

@ -0,0 +1,68 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
// Driving input ports from the instantiating scope via continuous assign
// is legal when port kind defaults to net (IEEE 1800-2023 23.2.2.3).
// All three forms below default to net for input ports.
// Scenario 1: bare input (defaults to net)
interface bare_if (input clk);
logic data;
endinterface
// Scenario 2: input with explicit data type (still net for input)
interface logic_if (input logic clk);
logic data;
endinterface
// Scenario 3: input with explicit net kind
interface wire_if (input wire clk);
logic data;
endinterface
module consumer (bare_if cif);
logic sampled;
always @(posedge cif.clk) sampled <= cif.data;
endmodule
module t;
logic clk = 0;
always #5 clk = ~clk;
integer cyc = 0;
bare_if bif(.clk());
assign bif.clk = clk;
assign bif.data = 1'b1;
logic_if lif(.clk());
assign lif.clk = clk;
assign lif.data = 1'b1;
wire_if wif(.clk());
assign wif.clk = clk;
assign wif.data = 1'b1;
consumer cons(.cif(bif));
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 10) begin
`checkh(bif.clk, clk);
`checkh(bif.data, 1'b1);
`checkh(lif.clk, clk);
`checkh(lif.data, 1'b1);
`checkh(wif.clk, clk);
`checkh(wif.data, 1'b1);
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,10 @@
%Error-ASSIGNIN: t/t_interface_input_port_assign_bad.v:16:10: Assigning to input/const variable: 'clk'
: ... note: In instance 't'
16 | assign clk = 1'b0;
| ^~~
... For error description see https://verilator.org/warn/ASSIGNIN?v=latest
%Error-ASSIGNIN: t/t_interface_input_port_assign_bad.v:23:14: Assigning to input/const variable: 'clk'
: ... note: In instance 't'
23 | assign vif.clk = sig;
| ^~~
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,28 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
// Case 1: Assigning to explicit var input port from OUTSIDE (illegal).
// Only 'input var' ports are variable kind (IEEE 1800-2023 23.2.2.3).
// Variable input ports cannot be assigned (IEEE 1800-2023 23.3.3.2).
interface var_if (input var logic clk);
endinterface
// Case 2: Assigning to net-type input port from INSIDE (illegal).
// Internal assign creates a second driver within the port's own scope.
interface internal_if (input wire clk);
assign clk = 1'b0; // ASSIGNIN: internal assign to net input
endinterface
module t;
logic sig;
internal_if iif(.clk(sig));
var_if vif(.clk());
assign vif.clk = sig; // ASSIGNIN: external assign to var input
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_interface_input_port_assign.v"
test.compile(timing_loop=True, verilator_flags2=['--timing'], v_flags2=['-fno-inline'])
test.execute()
test.passes()

View File

@ -10,11 +10,11 @@
]},
{"type":"MODULE","name":"mh6","addr":"(K)","loc":"d,26:8,26:11","origName":"mh6","verilogName":"mh6","level":1,"timeunit":"1ps","inlinesp": [],
"stmtsp": [
{"type":"VAR","name":"x_input_var_logic","addr":"(L)","loc":"d,26:23,26:40","dtypep":"(J)","origName":"x_input_var_logic","verilogName":"x_input_var_logic","isPrimaryIO":true,"direction":"INPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
{"type":"VAR","name":"x_input_var_logic","addr":"(L)","loc":"d,26:23,26:40","dtypep":"(J)","origName":"x_input_var_logic","verilogName":"x_input_var_logic","isPrimaryIO":true,"direction":"INPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"VAR","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
]},
{"type":"MODULE","name":"mh7","addr":"(M)","loc":"d,28:8,28:11","origName":"mh7","verilogName":"mh7","level":1,"timeunit":"1ps","inlinesp": [],
"stmtsp": [
{"type":"VAR","name":"x_input_var_integer","addr":"(N)","loc":"d,28:31,28:50","dtypep":"(G)","origName":"x_input_var_integer","verilogName":"x_input_var_integer","isPrimaryIO":true,"direction":"INPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"PORT","dtypeName":"integer","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
{"type":"VAR","name":"x_input_var_integer","addr":"(N)","loc":"d,28:31,28:50","dtypep":"(G)","origName":"x_input_var_integer","verilogName":"x_input_var_integer","isPrimaryIO":true,"direction":"INPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"VAR","dtypeName":"integer","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
]},
{"type":"MODULE","name":"mh8","addr":"(O)","loc":"d,30:8,30:11","origName":"mh8","verilogName":"mh8","level":1,"timeunit":"1ps","inlinesp": [],
"stmtsp": [
@ -22,7 +22,7 @@
]},
{"type":"MODULE","name":"mh9","addr":"(Q)","loc":"d,32:8,32:11","origName":"mh9","verilogName":"mh9","level":1,"timeunit":"1ps","inlinesp": [],
"stmtsp": [
{"type":"VAR","name":"x_output_var_logic","addr":"(R)","loc":"d,32:24,32:42","dtypep":"(J)","origName":"x_output_var_logic","verilogName":"x_output_var_logic","isPrimaryIO":true,"direction":"OUTPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
{"type":"VAR","name":"x_output_var_logic","addr":"(R)","loc":"d,32:24,32:42","dtypep":"(J)","origName":"x_output_var_logic","verilogName":"x_output_var_logic","isPrimaryIO":true,"direction":"OUTPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"VAR","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
]},
{"type":"MODULE","name":"mh10","addr":"(S)","loc":"d,34:8,34:12","origName":"mh10","verilogName":"mh10","level":1,"timeunit":"1ps","inlinesp": [],
"stmtsp": [
@ -42,12 +42,12 @@
]},
{"type":"MODULE","name":"mh17","addr":"(DB)","loc":"d,50:8,50:12","origName":"mh17","verilogName":"mh17","level":1,"timeunit":"1ps","inlinesp": [],
"stmtsp": [
{"type":"VAR","name":"x_input_var_integer","addr":"(EB)","loc":"d,50:31,50:50","dtypep":"(G)","origName":"x_input_var_integer","verilogName":"x_input_var_integer","isPrimaryIO":true,"direction":"INPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"PORT","dtypeName":"integer","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
{"type":"VAR","name":"x_input_var_integer","addr":"(EB)","loc":"d,50:31,50:50","dtypep":"(G)","origName":"x_input_var_integer","verilogName":"x_input_var_integer","isPrimaryIO":true,"direction":"INPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"VAR","dtypeName":"integer","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
{"type":"VAR","name":"y_input_wire_logic","addr":"(FB)","loc":"d,50:57,50:75","dtypep":"(J)","origName":"y_input_wire_logic","verilogName":"y_input_wire_logic","isPrimaryIO":true,"direction":"INPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"WIRE","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
]},
{"type":"MODULE","name":"mh18","addr":"(GB)","loc":"d,52:8,52:12","origName":"mh18","verilogName":"mh18","level":1,"timeunit":"1ps","inlinesp": [],
"stmtsp": [
{"type":"VAR","name":"x_output_var_logic","addr":"(HB)","loc":"d,52:24,52:42","dtypep":"(J)","origName":"x_output_var_logic","verilogName":"x_output_var_logic","isPrimaryIO":true,"direction":"OUTPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
{"type":"VAR","name":"x_output_var_logic","addr":"(HB)","loc":"d,52:24,52:42","dtypep":"(J)","origName":"x_output_var_logic","verilogName":"x_output_var_logic","isPrimaryIO":true,"direction":"OUTPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"VAR","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
{"type":"VAR","name":"y_input_wire_logic","addr":"(IB)","loc":"d,52:50,52:68","dtypep":"(J)","origName":"y_input_wire_logic","verilogName":"y_input_wire_logic","isPrimaryIO":true,"direction":"INPUT","isSigPublic":true,"lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
]},
{"type":"MODULE","name":"mh19","addr":"(JB)","loc":"d,54:8,54:12","origName":"mh19","verilogName":"mh19","level":1,"timeunit":"1ps","inlinesp": [],