Fix FSM detect unchecked casts and variable redeclaration (#7758)

This commit is contained in:
Adam Kostrzewski 2026-06-11 14:37:23 +02:00 committed by GitHub
parent 901909d3c7
commit 394c9bc9b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 736 additions and 13 deletions

View File

@ -9,6 +9,7 @@ Please see the Verilator manual for 200+ additional contributors. Thanks to all.
404allen404
Adam Bagley
Adam Kostrzewski
Adrian Sampson
Adrien Le Masle
أحمد المحمودي (Ahmed El-Mahmoudy)

View File

@ -1322,6 +1322,12 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) {
} else if (VN_IS(nodep, Sel)) {
nodep = VN_AS(nodep, Sel)->fromp();
continue;
} else if (VN_IS(nodep, AssocSel)) {
nodep = VN_AS(nodep, AssocSel)->fromp();
continue;
} else if (VN_IS(nodep, WildcardSel)) {
nodep = VN_AS(nodep, WildcardSel)->fromp();
continue;
} else if (overMembers && VN_IS(nodep, MemberSel)) {
nodep = VN_AS(nodep, MemberSel)->fromp();
continue;

View File

@ -530,8 +530,10 @@ class CoverageVisitor final : public VNVisitor {
newent.cleanup();
}
}
} else if (VN_IS(dtypep, QueueDType)) {
} else if (VN_IS(dtypep, QueueDType) || VN_IS(dtypep, AssocArrayDType)
|| VN_IS(dtypep, WildcardArrayDType)) {
// Not covered
varp->v3warn(COVERIGN, "Coverage ignored for type " << dtypep->prettyTypeName());
} else {
dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "
<< dtypep->prettyTypeName());

View File

@ -30,6 +30,7 @@
#include "V3Ast.h"
#include "V3Control.h"
#include "V3Graph.h"
#include "V3UniqueNames.h"
#include <algorithm>
#include <cctype>
@ -987,14 +988,23 @@ class FsmDetectVisitor final : public VNVisitor {
return assp;
}
static AstVarRef* tryExtractVarRef(AstNodeExpr* const exprp) {
AstVarRef* const varp = VN_CAST(AstArraySel::baseFromp(exprp, true), VarRef);
if (!varp) {
exprp->v3warn(COVERIGN,
"Ignoring unsupported: FSM coverage with " << exprp->prettyTypeName());
return nullptr;
}
return varp;
}
static AstNodeAssign* nodeStateVarAssign(AstNode* nodep, AstVarScope*& stateVscp,
AstVarScope*& fromVscp) {
AstNodeAssign* const assp = VN_CAST(nodep, NodeAssign);
if (!assp) return nullptr;
AstVarRef* const lhsp = VN_AS(assp->lhsp(), VarRef);
UASSERT_OBJ(lhsp, assp, "register commit lhs should be normalized to a VarRef");
AstVarRef* const lhsp = tryExtractVarRef(assp->lhsp());
AstVarRef* const rhsp = VN_CAST(assp->rhsp(), VarRef);
if (!rhsp) return nullptr;
if (!rhsp || !lhsp) return nullptr;
stateVscp = lhsp->varScopep();
fromVscp = rhsp->varScopep();
return assp;
@ -1006,11 +1016,9 @@ class FsmDetectVisitor final : public VNVisitor {
FsmStateValue& resetValue) {
AstNodeAssign* const assp = VN_CAST(nodep, NodeAssign);
if (!assp) return nullptr;
AstVarRef* const lhsp = VN_AS(assp->lhsp(), VarRef);
UASSERT_OBJ(lhsp, assp,
"conditional register commit lhs should be normalized to a VarRef");
AstVarRef* const lhsp = tryExtractVarRef(assp->lhsp());
AstCond* const rhsp = VN_CAST(assp->rhsp(), Cond);
if (!rhsp) return nullptr;
if (!rhsp || !lhsp) return nullptr;
if (AstVarRef* const elsep = VN_CAST(rhsp->elsep(), VarRef)) {
if (constValueStatus(rhsp->thenp(), resetValue) != ConstValueStatus::OK)
return nullptr;
@ -1033,7 +1041,7 @@ class FsmDetectVisitor final : public VNVisitor {
FsmStateValue& value) {
AstNodeAssign* const assp = VN_CAST(nodep, NodeAssign);
if (!assp) return nullptr;
AstVarRef* const lhsp = VN_AS(assp->lhsp(), VarRef);
AstVarRef* const lhsp = VN_CAST(AstArraySel::baseFromp(assp->lhsp(), true), VarRef);
UASSERT_OBJ(lhsp, assp,
"direct constant state assignment lhs should be normalized to a VarRef");
if (constValueStatus(assp->rhsp(), value) != ConstValueStatus::OK) return nullptr;
@ -1220,7 +1228,8 @@ class FsmDetectVisitor final : public VNVisitor {
AstVarRef* vrefp = VN_CAST(eqp->lhsp(), VarRef);
AstNodeExpr* valuep = eqp->rhsp();
if (!vrefp) {
vrefp = VN_AS(eqp->rhsp(), VarRef);
vrefp = tryExtractVarRef(eqp->rhsp());
if (!vrefp) { return false; }
valuep = eqp->lhsp();
}
@ -2082,6 +2091,7 @@ public:
class FsmLowerVisitor final {
// STATE - across all visitors
const FsmState& m_state;
V3UniqueNames m_fsmBuildNames;
// METHODS
// Rebuild a state-typed constant using the tracked state variable
@ -2133,8 +2143,8 @@ class FsmLowerVisitor final {
AstNodeModule* const modp = scopep->modp();
AstNodeDType* const prevDTypep = scopep->findLogicDType(
sampleVscp->width(), sampleVscp->width(), sampleVscp->dtypep()->numeric());
AstVarScope* const prevVscp
= scopep->createTemp("__Vfsmcov_prev__" + stateVscp->varp()->shortName(), prevDTypep);
const std::string tmpName = m_fsmBuildNames.get(stateVscp->varp()->shortName());
AstVarScope* const prevVscp = scopep->createTemp(tmpName, prevDTypep);
// The saved previous-state temp crosses the scheduler's pre/post split
// in the same way as Verilator's built-in NBA shadow variables, so keep
// both vars marked as post-life participants for stable MT ordering.
@ -2283,7 +2293,8 @@ public:
// concrete coverage instrumentation while the saved scoped pointers are
// still valid in the same pass.
explicit FsmLowerVisitor(const FsmState& state)
: m_state{state} {
: m_state{state}
, m_fsmBuildNames{"__Vfsmcov_prev"} {
for (const DetectedFsm& fsm : m_state.fsms()) { buildOne(*fsm.graphp); }
}
};

View File

@ -0,0 +1,6 @@
%Warning-COVERIGN: t/t_cover_fsm_concat_unsup.v:12:17: Ignoring unsupported: FSM coverage with CONCAT
12 | assign c = ({a, b} == 8'h00);
| ^
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: FSM coverage concat as unsupported operation test
#
# 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('vlt')
test.lint(fails=True, expect_filename=test.golden_filename, verilator_flags2=['--coverage'])
test.passes()

View File

@ -0,0 +1,18 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t (
input logic[6:0] a,
input logic b,
output logic c
);
assign c = ({a, b} == 8'h00);
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,99 @@
// // verilator_coverage annotation
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
package P;
typedef struct packed{
logic [7:0] vs;
} C;
typedef struct packed{
C a; int b;
} B;
typedef struct packed{
B a;
} A;
endpackage
module t (
%000009 input clk
);
typedef enum logic [1:0] {
S_IDLE = 2'd0,
S_RUN = 2'd1,
S_DONE = 2'd2,
S_ERR = 2'd3
} state_t;
%000001 logic rst;
%000001 logic start;
integer cyc;
%000001 state_t state /*verilator fsm_reset_arc*/;
%000002 P::A a;
%000001 logic done;
logic [7:0] va[int];
logic [7:0] va2d[int][int];
%000001 logic b;
%000001 logic c;
%000001 logic d;
assign b = (a.a.a.vs == 8'h0);
assign c = (va[0] == 8'h0);
assign d = (va2d[0][0] == 8'h0);
%000001 initial begin
%000001 rst = 1'b1;
%000001 start = 1'b0;
%000001 cyc = 0;
end
%000009 always @(posedge clk) begin
%000009 cyc <= cyc + 1;
%000008 if (cyc == 1) rst <= 1'b0;
%000008 if (cyc == 2) start <= 1'b1;
%000008 if (cyc == 3) start <= 1'b0;
%000008 if (cyc == 8) begin
%000001 $write("*-* All Finished *-*\n");
%000001 $finish;
end
end
%000009 always_ff @(posedge clk) begin
%000007 if (rst) begin
%000002 state <= S_IDLE;
end
%000007 else begin
%000007 case (state)
// [FSM coverage]
%000001 // [fsm_arc t.state::ANY->S_IDLE[reset_include]] [reset arc, excluded from %]
%000002 // [fsm_arc t.state::S_DONE->S_DONE]
%000003 // [fsm_arc t.state::S_IDLE->S_IDLE]
%000001 // [fsm_arc t.state::S_IDLE->S_RUN]
%000001 // [fsm_state t.state::S_DONE]
%000000 // [fsm_state t.state::S_ERR] *** UNCOVERED ***
%000000 // [fsm_state t.state::S_IDLE] *** UNCOVERED ***
%000001 // [fsm_state t.state::S_RUN]
%000002 S_IDLE:
%000001 if (start) state <= S_RUN;
%000001 else state <= S_IDLE;
%000003 S_RUN: begin
%000003 a.a.a.vs <= a.a.a.vs + 1;
%000003 done <= (a.a.a.vs == 8'h1);
%000002 if (done) begin
%000001 state <= S_DONE;
%000002 end else begin
%000002 state <= S_RUN;
end
end
%000002 S_DONE: state <= S_DONE;
%000000 default: state <= S_ERR;
endcase
end
end
endmodule

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: FSM coverage array sel test
#
# 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 os
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=["--coverage"])
test.execute()
test.run(cmd=[
os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage",
"--annotate",
test.obj_dir + "/annotated",
test.obj_dir + "/coverage.dat",
],
verilator_run=True)
test.files_identical(test.obj_dir + "/annotated/" + test.name + ".v", test.golden_filename)
test.passes()

View File

@ -0,0 +1,88 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
package P;
typedef struct packed{
logic [7:0] vs;
} C;
typedef struct packed{
C a; int b;
} B;
typedef struct packed{
B a;
} A;
endpackage
module t (
input clk
);
typedef enum logic [1:0] {
S_IDLE = 2'd0,
S_RUN = 2'd1,
S_DONE = 2'd2,
S_ERR = 2'd3
} state_t;
logic rst;
logic start;
integer cyc;
state_t state /*verilator fsm_reset_arc*/;
P::A a;
logic done;
logic [7:0] va[int];
logic [7:0] va2d[int][int];
logic b;
logic c;
logic d;
assign b = (a.a.a.vs == 8'h0);
assign c = (va[0] == 8'h0);
assign d = (va2d[0][0] == 8'h0);
initial begin
rst = 1'b1;
start = 1'b0;
cyc = 0;
end
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 1) rst <= 1'b0;
if (cyc == 2) start <= 1'b1;
if (cyc == 3) start <= 1'b0;
if (cyc == 8) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
always_ff @(posedge clk) begin
if (rst) begin
state <= S_IDLE;
end
else begin
case (state)
S_IDLE:
if (start) state <= S_RUN;
else state <= S_IDLE;
S_RUN: begin
a.a.a.vs <= a.a.a.vs + 1;
done <= (a.a.a.vs == 8'h1);
if (done) begin
state <= S_DONE;
end else begin
state <= S_RUN;
end
end
S_DONE: state <= S_DONE;
default: state <= S_ERR;
endcase
end
end
endmodule

View File

@ -0,0 +1,89 @@
// // verilator_coverage annotation
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t #(
parameter int unsigned W = 16,
parameter int unsigned D = 4,
parameter int unsigned BW = 2
) (
%000009 input clk
);
typedef enum logic [1:0] {
S_IDLE = 2'd0,
S_RUN = 2'd1,
S_DONE = 2'd2,
S_ERR = 2'd3
} state_t;
%000001 logic rst;
%000001 logic start;
integer cyc;
%000001 state_t state /*verilator fsm_reset_arc*/;
%000001 logic [1:0] done_arr;
%000001 logic [W-1:0] a;
%000000 logic [BW-1:0] b;
begin
%000001 logic [D-1:0][W-1:0] s;
begin
%000009 always_ff @(posedge clk)
%000009 s[b] <= a;
end
end
%000001 initial begin
%000001 rst = 1'b1;
%000001 start = 1'b0;
%000001 cyc = 0;
end
%000009 always @(posedge clk) begin
%000009 cyc <= cyc + 1;
%000008 if (cyc == 1) rst <= 1'b0;
%000008 if (cyc == 2) start <= 1'b1;
%000008 if (cyc == 3) start <= 1'b0;
%000008 if (cyc == 4) a[0] = 1'b1;
%000008 if (cyc == 8) begin
%000001 $write("*-* All Finished *-*\n");
%000001 $finish;
end
end
%000009 always_ff @(posedge clk) begin
%000007 if (rst) begin
%000002 state <= S_IDLE;
end
%000007 else begin
%000007 case (state)
// [FSM coverage]
%000001 // [fsm_arc t.state::ANY->S_IDLE[reset_include]] [reset arc, excluded from %]
%000002 // [fsm_arc t.state::S_DONE->S_DONE]
%000003 // [fsm_arc t.state::S_IDLE->S_IDLE]
%000001 // [fsm_arc t.state::S_IDLE->S_RUN]
%000001 // [fsm_state t.state::S_DONE]
%000000 // [fsm_state t.state::S_ERR] *** UNCOVERED ***
%000000 // [fsm_state t.state::S_IDLE] *** UNCOVERED ***
%000001 // [fsm_state t.state::S_RUN]
%000002 S_IDLE:
%000001 if (start) state <= S_RUN;
%000001 else state <= S_IDLE;
%000003 S_RUN: begin;
%000003 done_arr[0] <= (a[0] == 1'b1);
%000002 if (done_arr[0]) begin
%000001 state <= S_DONE;
%000002 end else begin
%000002 state <= S_RUN;
end
end
%000002 S_DONE: state <= S_DONE;
%000000 default: state <= S_ERR;
endcase
end
end
endmodule

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: FSM coverage assignment test
#
# 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 os
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=["--coverage"])
test.execute()
test.run(cmd=[
os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage",
"--annotate",
test.obj_dir + "/annotated",
test.obj_dir + "/coverage.dat",
],
verilator_run=True)
test.files_identical(test.obj_dir + "/annotated/" + test.name + ".v", test.golden_filename)
test.passes()

View File

@ -0,0 +1,78 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t #(
parameter int unsigned W = 16,
parameter int unsigned D = 4,
parameter int unsigned BW = 2
) (
input clk
);
typedef enum logic [1:0] {
S_IDLE = 2'd0,
S_RUN = 2'd1,
S_DONE = 2'd2,
S_ERR = 2'd3
} state_t;
logic rst;
logic start;
integer cyc;
state_t state /*verilator fsm_reset_arc*/;
logic [1:0] done_arr;
logic [W-1:0] a;
logic [BW-1:0] b;
begin
logic [D-1:0][W-1:0] s;
begin
always_ff @(posedge clk)
s[b] <= a;
end
end
initial begin
rst = 1'b1;
start = 1'b0;
cyc = 0;
end
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 1) rst <= 1'b0;
if (cyc == 2) start <= 1'b1;
if (cyc == 3) start <= 1'b0;
if (cyc == 4) a[0] = 1'b1;
if (cyc == 8) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
always_ff @(posedge clk) begin
if (rst) begin
state <= S_IDLE;
end
else begin
case (state)
S_IDLE:
if (start) state <= S_RUN;
else state <= S_IDLE;
S_RUN: begin;
done_arr[0] <= (a[0] == 1'b1);
if (done_arr[0]) begin
state <= S_DONE;
end else begin
state <= S_RUN;
end
end
S_DONE: state <= S_DONE;
default: state <= S_ERR;
endcase
end
end
endmodule

View File

@ -0,0 +1,11 @@
%Warning-COVERIGN: t/t_cover_fsm_sel_togglevar_unsup.v:20:14: Coverage ignored for type ASSOCARRAYDTYPE
: ... note: In instance 't'
20 | input P::A a,
| ^
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
%Warning-COVERIGN: t/t_cover_fsm_sel_togglevar_unsup.v:20:14: Coverage ignored for type WILDCARDARRAYDTYPE
: ... note: In instance 't'
20 | input P::A a,
| ^
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: FSM coverage ignores associative array selection operators
#
# 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('vlt')
test.lint(fails=True, expect_filename=test.golden_filename, verilator_flags2=['--coverage'])
test.passes()

View File

@ -0,0 +1,31 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
package P;
typedef struct {
logic [7:0] va[int];
logic [7:0] vw[*];
} C;
typedef struct {
C a; int b;
} B;
typedef struct {
B a;
} A;
endpackage
module t (
input P::A a,
output logic b,
output logic c
);
assign b = (a.a.a.va[0] == 8'h0);
assign c = (a.a.a.vw[0] == 8'h0);
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: FSM no duplicate variables test
#
# 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(verilator_flags2=["--coverage -Wno-PINMISSING"])
test.execute()
test.passes()

View File

@ -0,0 +1,173 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
module rr
#(
) (
input logic clk,
input logic rst,
input logic [7:0] data,
input logic data_q
);
logic a;
logic [15:0] dcnt;
typedef enum logic [7:0] {
S0,
S1,
S2,
S3
} state_t;
state_t state_d, state_q;
always_ff @(posedge clk or negedge rst)
if (!rst) state_q <= S0;
always_ff @(posedge clk)
unique case (state_q)
S1: if (a) dcnt[7:0] <= data;
S2: if (a) dcnt[15:8] <= data;
S3: if (data_q) dcnt <= dcnt - 1;
default: dcnt <= dcnt;
endcase
endmodule
module re
#(
) (
input logic clk,
input logic rst,
output logic o,
input unused0, /* block optimizations */
input unused1,
input unused2,
input unused3,
input unused4,
input unused5,
input unused6,
input unused7,
input unused8,
input unused9,
input unused10,
input unused11,
input unused12,
input unused13,
input unused14,
input unused15,
input unused16,
input unused17,
input unused18,
input unused19,
input unused20,
input unused21,
input unused22,
input unused23,
input unused24,
input unused25,
input unused26,
input unused27,
input unused28,
input unused29,
input unused30,
input unused31,
input unused32,
input unused33,
input unused34,
input unused35,
input unused36,
input unused37,
input unused38,
input unused39,
input unused40
);
logic [15:0] dcnt;
typedef enum logic [7:0] {
S0,
S1
} state_t;
state_t state_d, state_q;
always_ff @(posedge clk or negedge rst)
if (!rst) state_q <= S0;
always_ff @(posedge clk)
unique case (state_q)
S1: o <= dcnt[0];
default: o <= '0;
endcase
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module rh
#(
) (
input logic clk
);
logic [7:0] a;
logic b;
logic c;
logic d;
logic rst;
rr xrr (
.clk,
.rst(rst),
.data (a),
.data_q (b & c)
);
re xre (
.clk,
.rst(rst),
.o (d)
);
endmodule
module U
#(
) (
input clk,
input rst
);
rh xrh (
.clk (clk)
);
endmodule
module C #(
) (
input clk,
input rst
);
U U (
.clk,
.rst
);
endmodule
module A #(
) (
);
logic clk;
logic rst;
C c0 (
.clk,
.rst
);
C c1 (
.clk,
.rst
);
endmodule
module B #(
) (
);
logic clk;
logic rst;
C xC (
.clk,
.rst
);
endmodule
module t #(
) (
);
B b (
);
A a (
);
endmodule