diff --git a/docs/guide/verilating.rst b/docs/guide/verilating.rst index af0b8df25..39193096f 100644 --- a/docs/guide/verilating.rst +++ b/docs/guide/verilating.rst @@ -143,8 +143,11 @@ Limitations Hierarchy blocks have some limitations, including: -* The hierarchy block cannot be accessed using dot (.) from the upper - module(s) or other hierarchy blocks. +* Internals of the hierarchy block cannot be accessed using dot (.) from + the upper module(s) or other hierarchy blocks, except that ports of a + hierarchy block instance can be accessed from the directly enclosing + nested hierarchy block, or from the top level non-hierarchical portions + of the design if not a nested hierarchy block. * Modport cannot be used at the hierarchical block boundary. diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 1118e4a39..0ca440d11 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -729,8 +729,8 @@ public: if (lookupSymp) { if (const AstCell* const cellp = VN_CAST(lookupSymp->nodep(), Cell)) { if (const AstNodeModule* const modp = cellp->modp()) { - if (modp->hierBlock()) { - refLocationp->v3error("Cannot access inside hierarchical block"); + if (modp->hierBlock() && !leftname.empty()) { + refLocationp->v3error("Cannot access scope inside hierarchical block"); } else if (VN_IS(modp, NotFoundModule)) { refLocationp->v3error("Dotted reference to instance that refers to " "missing module/interface: " @@ -3004,6 +3004,22 @@ class LinkDotResolveVisitor final : public VNVisitor { } } + // Returns true and issues error iff 'varp' cannot be accesed with a VarXRef + // in a hier_block given by 'scopeSymp' + bool errorHierNonPort(AstVarXRef* refp, AstVar* varp, VSymEnt* scopeSymp) { + // OK to access ports on hier_block + if (varp->isIO()) return false; + // OK if not an instance + const AstCell* const cellp = VN_CAST(scopeSymp->nodep(), Cell); + if (!cellp) return false; + // Ok if not a hier_block + const AstNodeModule* const modp = cellp->modp(); + if (!modp->hierBlock()) return false; + // Bad + refp->v3error("Cannot access non-port symbols inside hierarchical block"); + return true; + } + #define LINKDOT_VISIT_START() \ VL_RESTORER(m_indent); \ ++m_indent; @@ -4104,6 +4120,8 @@ class LinkDotResolveVisitor final : public VNVisitor { nodep->varp(varp); updateVarUse(nodep->varp()); UINFO(7, indent() << "Resolved " << nodep); // Also prints varp + // If found, check if it's ok to access in case it's in a hier_block + if (nodep->varp() && errorHierNonPort(nodep, nodep->varp(), dotSymp)) return; if (!nodep->varp()) { nodep->v3error("Can't find definition of " << AstNode::prettyNameQ(baddot) << " in dotted signal: '" @@ -4130,6 +4148,8 @@ class LinkDotResolveVisitor final : public VNVisitor { VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot, true); AstVarScope* vscp = foundp ? VN_AS(foundp->nodep(), VarScope) : nullptr; + // If found, check if it's ok to access in case it's in a hier_block + if (vscp && errorHierNonPort(nodep, vscp->varp(), dotSymp)) return; if (!vscp) { nodep->v3error("Can't find varpin scope of " << AstNode::prettyNameQ(baddot) << " in dotted signal: '" diff --git a/test_regress/t/t_hier_block.v b/test_regress/t/t_hier_block.v index d21be731f..7cf2e715a 100644 --- a/test_regress/t/t_hier_block.v +++ b/test_regress/t/t_hier_block.v @@ -55,7 +55,9 @@ module t (/*AUTOARG*/ always_ff @(posedge clk) begin if (out3 != out3_2) $stop; - $display("%d out0:%d %d %d %d %d", count, out0, out1, out2, out3, out5, out6); + $display("%d %m out0:%d %d %d %d %d", count, out0, out1, out2, out3, out5, out6); + $display("%d %m child input ports: %d %d %d", count, i_sub1.in, i_sub2.in, i_sub3.in); + $display("%d %m child output ports: %d %d %d", count, i_sub1.out, i_sub2.out, i_sub3.out); if (count == 16) begin if (out6 == 19) begin $write("*-* All Finished *-*\n"); @@ -192,8 +194,16 @@ module sub3 #( assign out = out4; /* verilator lint_off REALCVT */ sub4 #(.P0(1.6), .P1(3.1), .P3(4.1)) i_sub4_0(.clk(clk), .in(ff), .out(out4)); // incr 2 - sub4 #(.P0(2.4), .P1(3.1), .P3(5)) i_sub4_1(.clk(clk), .in(ff), .out(out4_2)); + sub4 #(.P0(2.4), .P1(3.1), .P3(5)) i_sub4_1(.clk(clk), .in(), .out(out4_2)); /* verilator lint_on REALCVT */ + /* verilator lint_off ASSIGNIN */ + assign i_sub4_1.in = ff; // Hierarchical reference to port of hier_block is OK + /* verilator lint_off ASSIGNIN */ + + always @(posedge clk) begin + $display("%d %m child input ports: %d %d", $time, i_sub4_0.in, i_sub4_1.in); + $display("%d %m child output ports: %d %d", $time, i_sub4_0.out, i_sub4_1.out); + end endmodule module sub4 #( @@ -248,6 +258,10 @@ module sub4 #( $display("in[%d][%d] act:%d exp:%d", i, j, sub5_out[i][j], exp); $stop; end + if (i_sub5.out[i][j] != exp) begin + $display("in[%d][%d] act:%d exp:%d", i, j, i_sub5.out[i][j], exp); + $stop; + end end end end diff --git a/test_regress/t/t_hier_block0_bad.out b/test_regress/t/t_hier_block0_bad.out index 8912e1cde..5d125ae97 100644 --- a/test_regress/t/t_hier_block0_bad.out +++ b/test_regress/t/t_hier_block0_bad.out @@ -3,7 +3,15 @@ 21 | sub0 #(UNPACKED) i_sub0(.clk(clk), .in(8'(count)), .out(out0)); | ^~~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_hier_block0_bad.v:26:42: Cannot access inside hierarchical block - 26 | $display("%d %d %d", count, i_sub0.ff, out1); - | ^~ +%Error: t/t_hier_block0_bad.v:62:41: Cannot access non-port symbols inside hierarchical block + : ... note: In instance 't.i_sub0' + 62 | $display("%m: i_sub.x: %d", i_sub.x); + | ^ +%Error: t/t_hier_block0_bad.v:26:50: Cannot access non-port symbols inside hierarchical block + : ... note: In instance 't' + 26 | $display("%d i_sub0.ff: %d", count, i_sub0.ff); + | ^~ +%Error: t/t_hier_block0_bad.v:27:63: Cannot access scope inside hierarchical block + 27 | $display("%d i_sub0.i_sub.out: %d", count, i_sub0.i_sub.out); + | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_hier_block0_bad.v b/test_regress/t/t_hier_block0_bad.v index efda65425..75019af6e 100644 --- a/test_regress/t/t_hier_block0_bad.v +++ b/test_regress/t/t_hier_block0_bad.v @@ -22,8 +22,12 @@ module t (/*AUTOARG*/ sub1 #(.T(logic[7:0])) i_sub1(.in(out0), .out(out1)); always_ff @(posedge clk) begin - // dotted access under hierarchical block is not allowed - $display("%d %d %d", count, i_sub0.ff, out1); + // dotted access under hierarchical block is not allowed ... + $display("%d i_sub0.ff: %d", count, i_sub0.ff); + $display("%d i_sub0.i_sub.out: %d", count, i_sub0.i_sub.out); + // ... Except for ports on a dierct hierarchical child + $display("%d i_sub0.out: %d", count, i_sub0.out); + $display("%d out1: %d", count, out1); if (count == 16) begin if (out1 == 15) begin $write("*-* All Finished *-*\n"); @@ -49,6 +53,16 @@ module sub0 #( always_ff @(posedge clk) ff <= in; assign out = ff; + + logic [7:0] gg; + sub0_sub0 i_sub(.in(ff), .out(gg)); + + always_ff @(posedge clk) begin + // dotted access under hierarchical block is not allowed ... + $display("%m: i_sub.x: %d", i_sub.x); + // ... Except for ports on a direct hierarchical child + $display("%m: i_sub.out: %d", i_sub.out); + end endmodule module sub1 #( @@ -56,4 +70,15 @@ module sub1 #( ) ( input wire T in, output wire T out); `HIER_BLOCK assign out = in; + + sub1_sub #(T) sub(in, out); +endmodule + +module sub0_sub0 ( + input wire [7:0] in, + output wire [7:0] out +); + `HIER_BLOCK + wire [7:0] x = in + 1; + assign out = x; endmodule