Co-authored-by: Paul Swirhun <paulswirhun@gmail.com>
This commit is contained in:
parent
847de990de
commit
e27613ed45
|
|
@ -1366,20 +1366,22 @@ class ParamVisitor final : public VNVisitor {
|
||||||
m_iterateModule = false;
|
m_iterateModule = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract the base reference name from a dotted VarXRef (e.g., "iface.FOO" -> "iface")
|
||||||
|
string getRefBaseName(const AstVarXRef* refp) {
|
||||||
|
const string dotted = refp->dotted();
|
||||||
|
if (dotted.empty()) return "";
|
||||||
|
return dotted.substr(0, dotted.find('.'));
|
||||||
|
}
|
||||||
|
|
||||||
void checkParamNotHier(AstNode* valuep) {
|
void checkParamNotHier(AstNode* valuep) {
|
||||||
if (!valuep) return;
|
if (!valuep) return;
|
||||||
valuep->foreachAndNext([&](const AstNodeExpr* exprp) {
|
valuep->foreachAndNext([&](const AstNodeExpr* exprp) {
|
||||||
if (const AstVarXRef* const refp = VN_CAST(exprp, VarXRef)) {
|
if (const AstVarXRef* const refp = VN_CAST(exprp, VarXRef)) {
|
||||||
// Allow hierarchical ref to interface params through interface/modport ports
|
// Allow hierarchical ref to interface params through interface/modport ports
|
||||||
// (always), and also allow to locally declared interfaces if the user has
|
|
||||||
// waived HIERPARAM.
|
|
||||||
bool isIfacePortRef = false;
|
bool isIfacePortRef = false;
|
||||||
if (refp->varp() && refp->varp()->isIfaceParam()) {
|
if (refp->varp() && refp->varp()->isIfaceParam()) {
|
||||||
const string dotted = refp->dotted();
|
const string refname = getRefBaseName(refp);
|
||||||
if (!dotted.empty()) {
|
isIfacePortRef = !refname.empty() && m_ifacePortNames.count(refname);
|
||||||
const string refname = dotted.substr(0, dotted.find('.'));
|
|
||||||
isIfacePortRef = m_ifacePortNames.count(refname);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isIfacePortRef) {
|
if (!isIfacePortRef) {
|
||||||
|
|
@ -1395,6 +1397,23 @@ class ParamVisitor final : public VNVisitor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if cell parameters reference interface ports
|
||||||
|
bool cellParamsReferenceIfacePorts(AstCell* cellp) {
|
||||||
|
if (!cellp->paramsp()) return false;
|
||||||
|
|
||||||
|
for (AstNode* nodep = cellp->paramsp(); nodep; nodep = nodep->nextp()) {
|
||||||
|
if (AstPin* const pinp = VN_CAST(nodep, Pin)) {
|
||||||
|
if (AstNode* const exprp = pinp->exprp()) {
|
||||||
|
if (const AstVarXRef* const refp = VN_CAST(exprp, VarXRef)) {
|
||||||
|
const string refname = getRefBaseName(refp);
|
||||||
|
if (!refname.empty() && m_ifacePortNames.count(refname)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// A generic visitor for cells and class refs
|
// A generic visitor for cells and class refs
|
||||||
void visitCellOrClassRef(AstNode* nodep, bool isIface) {
|
void visitCellOrClassRef(AstNode* nodep, bool isIface) {
|
||||||
// Must do ifaces first, so push to list and do in proper order
|
// Must do ifaces first, so push to list and do in proper order
|
||||||
|
|
@ -1402,10 +1421,13 @@ class ParamVisitor final : public VNVisitor {
|
||||||
nodep->user2p(&m_strings.back());
|
nodep->user2p(&m_strings.back());
|
||||||
|
|
||||||
// For interface cells with parameters, specialize first before processing children
|
// For interface cells with parameters, specialize first before processing children
|
||||||
|
// Only do early specialization if parameters don't reference interface ports
|
||||||
if (isIface && VN_CAST(nodep, Cell) && VN_CAST(nodep, Cell)->paramsp()) {
|
if (isIface && VN_CAST(nodep, Cell) && VN_CAST(nodep, Cell)->paramsp()) {
|
||||||
AstCell* const cellp = VN_CAST(nodep, Cell);
|
AstCell* const cellp = VN_CAST(nodep, Cell);
|
||||||
AstNodeModule* const srcModp = cellp->modp();
|
if (!cellParamsReferenceIfacePorts(cellp)) {
|
||||||
m_processor.nodeDeparam(cellp, srcModp, m_modp, m_modp->someInstanceName());
|
AstNodeModule* const srcModp = cellp->modp();
|
||||||
|
m_processor.nodeDeparam(cellp, srcModp, m_modp, m_modp->someInstanceName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visit parameters in the instantiation.
|
// Visit parameters in the instantiation.
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,7 @@ module submod_modport (
|
||||||
assign result = mp.FOO + mp.BAR;
|
assign result = mp.FOO + mp.BAR;
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
// Test module that asserts interface parameter values - catches dependency bugs
|
module submod_assert2 #(
|
||||||
module submod_assert #(
|
|
||||||
parameter int EXPECTED_FOO = 0,
|
parameter int EXPECTED_FOO = 0,
|
||||||
parameter int EXPECTED_BAR = 0
|
parameter int EXPECTED_BAR = 0
|
||||||
) (
|
) (
|
||||||
|
|
@ -51,6 +50,128 @@ module submod_assert #(
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
// Test module that asserts interface parameter values - catches dependency bugs
|
||||||
|
module submod_assert #(
|
||||||
|
parameter int EXPECTED_FOO = 0,
|
||||||
|
parameter int EXPECTED_BAR = 0
|
||||||
|
) (
|
||||||
|
TEST_IF iface
|
||||||
|
);
|
||||||
|
// Make a new interface with inherited parameters and pass it to a submodule
|
||||||
|
// with inherited expectations.
|
||||||
|
TEST_IF #(
|
||||||
|
.FOO(iface.FOO),
|
||||||
|
.BAR(iface.BAR)
|
||||||
|
) iface2 ();
|
||||||
|
|
||||||
|
// Test mixed parameters: constant + interface port reference
|
||||||
|
TEST_IF #(
|
||||||
|
.FOO(7), // Constant parameter
|
||||||
|
.BAR(iface.FOO) // References interface port
|
||||||
|
) iface_mixed ();
|
||||||
|
|
||||||
|
// Test specifying only FOO parameter (BAR should use default calculation)
|
||||||
|
TEST_IF #(
|
||||||
|
.FOO(iface.FOO) // Only FOO specified, BAR = FOO * 10
|
||||||
|
) iface_foo_only ();
|
||||||
|
|
||||||
|
// Test specifying only BAR parameter (FOO should use default)
|
||||||
|
TEST_IF #(
|
||||||
|
.BAR(iface.BAR) // Only BAR specified, FOO = default (1)
|
||||||
|
) iface_bar_only ();
|
||||||
|
|
||||||
|
// Test no parameters specified (both should use defaults)
|
||||||
|
TEST_IF iface_defaults ();
|
||||||
|
|
||||||
|
submod_assert2 #(
|
||||||
|
.EXPECTED_FOO(EXPECTED_FOO),
|
||||||
|
.EXPECTED_BAR(EXPECTED_BAR)
|
||||||
|
) u_submod_assert2 (
|
||||||
|
.iface(iface2)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test the mixed parameter interface
|
||||||
|
submod_assert2 #(
|
||||||
|
.EXPECTED_FOO(7),
|
||||||
|
.EXPECTED_BAR(EXPECTED_FOO) // BAR should get iface.FOO value
|
||||||
|
) u_mixed_assert (
|
||||||
|
.iface(iface_mixed)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test FOO-only interface
|
||||||
|
submod_assert2 #(
|
||||||
|
.EXPECTED_FOO(EXPECTED_FOO),
|
||||||
|
.EXPECTED_BAR(EXPECTED_FOO * 10) // BAR = FOO * 10
|
||||||
|
) u_foo_only_assert (
|
||||||
|
.iface(iface_foo_only)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test BAR-only interface
|
||||||
|
submod_assert2 #(
|
||||||
|
.EXPECTED_FOO(1), // FOO = default
|
||||||
|
.EXPECTED_BAR(EXPECTED_BAR) // BAR = specified value
|
||||||
|
) u_bar_only_assert (
|
||||||
|
.iface(iface_bar_only)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test defaults interface
|
||||||
|
submod_assert2 #(
|
||||||
|
.EXPECTED_FOO(1), // FOO = default
|
||||||
|
.EXPECTED_BAR(10) // BAR = FOO * 10 = 1 * 10
|
||||||
|
) u_defaults_assert (
|
||||||
|
.iface(iface_defaults)
|
||||||
|
);
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// Verify the dependent parameter BAR is correctly computed in the module
|
||||||
|
if (iface.FOO != EXPECTED_FOO) begin
|
||||||
|
$error("FOO mismatch in module: expected %0d, got %0d", EXPECTED_FOO, iface.FOO);
|
||||||
|
end
|
||||||
|
if (iface.BAR != EXPECTED_BAR) begin
|
||||||
|
$error("BAR dependency failed in module: expected %0d, got %0d", EXPECTED_BAR, iface.BAR);
|
||||||
|
end
|
||||||
|
|
||||||
|
// Verify all interface instances have correct parameter values
|
||||||
|
if (iface2.FOO != EXPECTED_FOO) begin
|
||||||
|
$error("iface2.FOO mismatch: expected %0d, got %0d", EXPECTED_FOO, iface2.FOO);
|
||||||
|
end
|
||||||
|
if (iface2.BAR != EXPECTED_BAR) begin
|
||||||
|
$error("iface2.BAR mismatch: expected %0d, got %0d", EXPECTED_BAR, iface2.BAR);
|
||||||
|
end
|
||||||
|
|
||||||
|
if (iface_mixed.FOO != 7) begin
|
||||||
|
$error("iface_mixed.FOO mismatch: expected 7, got %0d", iface_mixed.FOO);
|
||||||
|
end
|
||||||
|
if (iface_mixed.BAR != EXPECTED_FOO) begin
|
||||||
|
$error("iface_mixed.BAR mismatch: expected %0d, got %0d", EXPECTED_FOO, iface_mixed.BAR);
|
||||||
|
end
|
||||||
|
|
||||||
|
if (iface_foo_only.FOO != EXPECTED_FOO) begin
|
||||||
|
$error("iface_foo_only.FOO mismatch: expected %0d, got %0d", EXPECTED_FOO,
|
||||||
|
iface_foo_only.FOO);
|
||||||
|
end
|
||||||
|
if (iface_foo_only.BAR != EXPECTED_FOO * 10) begin
|
||||||
|
$error("iface_foo_only.BAR mismatch: expected %0d, got %0d", EXPECTED_FOO * 10,
|
||||||
|
iface_foo_only.BAR);
|
||||||
|
end
|
||||||
|
|
||||||
|
if (iface_bar_only.FOO != 1) begin
|
||||||
|
$error("iface_bar_only.FOO mismatch: expected 1, got %0d", iface_bar_only.FOO);
|
||||||
|
end
|
||||||
|
if (iface_bar_only.BAR != EXPECTED_BAR) begin
|
||||||
|
$error("iface_bar_only.BAR mismatch: expected %0d, got %0d", EXPECTED_BAR,
|
||||||
|
iface_bar_only.BAR);
|
||||||
|
end
|
||||||
|
|
||||||
|
if (iface_defaults.FOO != 1) begin
|
||||||
|
$error("iface_defaults.FOO mismatch: expected 1, got %0d", iface_defaults.FOO);
|
||||||
|
end
|
||||||
|
if (iface_defaults.BAR != 10) begin
|
||||||
|
$error("iface_defaults.BAR mismatch: expected 10, got %0d", iface_defaults.BAR);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
// Test parameterized interface chain: module parameter -> interface parameter -> submodule
|
// Test parameterized interface chain: module parameter -> interface parameter -> submodule
|
||||||
module param_chain #(
|
module param_chain #(
|
||||||
parameter int TOP_PARAM = 3
|
parameter int TOP_PARAM = 3
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ endmodule
|
||||||
|
|
||||||
|
|
||||||
module testmod
|
module testmod
|
||||||
|
#(parameter SOME_PARAM = 789)
|
||||||
(
|
(
|
||||||
input clk,
|
input clk,
|
||||||
test_if.mp intf,
|
test_if.mp intf,
|
||||||
|
|
@ -77,6 +78,8 @@ module testmod
|
||||||
test_if.mp intf_array [1:0]
|
test_if.mp intf_array [1:0]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test_if #(.FOO (intf.FOO)) some_other_intf ();
|
||||||
|
|
||||||
// verilator lint_off HIERPARAM
|
// verilator lint_off HIERPARAM
|
||||||
localparam THE_FOO = intf.FOO;
|
localparam THE_FOO = intf.FOO;
|
||||||
localparam THE_OTHER_FOO = intf_no_mp.FOO;
|
localparam THE_OTHER_FOO = intf_no_mp.FOO;
|
||||||
|
|
@ -91,6 +94,10 @@ module testmod
|
||||||
$display("%%Error: THE_FOO = %0d", THE_FOO);
|
$display("%%Error: THE_FOO = %0d", THE_FOO);
|
||||||
$stop;
|
$stop;
|
||||||
end
|
end
|
||||||
|
if (some_other_intf.FOO != 5) begin
|
||||||
|
$display("%%Error: some_other_intf.FOO = %0d", some_other_intf.FOO);
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
if (THE_OTHER_FOO != 5) begin
|
if (THE_OTHER_FOO != 5) begin
|
||||||
$display("%%Error: THE_OTHER_FOO = %0d", THE_OTHER_FOO);
|
$display("%%Error: THE_OTHER_FOO = %0d", THE_OTHER_FOO);
|
||||||
$stop;
|
$stop;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue