Repository: cocotb/cocotb

Issue:
Hierarchy access for generate statement
When we instantiate some block with generate statement. 
How to access that hierarchical module?

example) When I want to access "SubBlock[3].u_SubModule.a". 
genvar i;
generate 
  for(i=0;i<5;i=i+1) begin : SubBlock
    SubModule u_SubModule(
       .a(...),
       .b(...),
    );
  end
endgenerate

First, I tried to do this 
RisingEdge(dut.SubBlock[3].u_SubModule.a) -> cocotb can't find SubBlock

and, Second
RisingEdge(dut.u_SubModule.a) -> cocotb find their object. but, which module accessed? 
                                                         I instantiated five modules.




Discussion:
The first way should work I believe.  What simulator are you using?

You should try running test_array.  It tests something similar. 

I tried to running test_array using VCS. It's similar. but, different.

So, I want to make this case test into test_cases. Can I make it? 
It is first time to make test case and push request. Is it okay?

If you are still having trouble create a new issue.
I believe there is still an issue with hierarchy access - please see this MVCE
https://github.com/mattvenn/cocotb-mvce-generate-bug/blob/master/test.py
This is with icarus 10.3 compiled from source
Running with debugging I get [this](https://github.com/cocotb/cocotb/files/5255524/output.log). If I set line 9 to `True`, the test passes and I get [this](https://github.com/cocotb/cocotb/files/5255513/output.log). I'll look into this more later, but there is definitely a problem with the discovery of pseudo-regions. Calling in the expert on pseudo-regions @garmin-mjames.
I see the problem and I'm astonished that this hasn't been caught before...

When attempting to access `genblk1`, the VPI implementation is unaware that it is a pseudo-region, causing `vpi_handle_by_name` to fail. When iterating over "top" beforehand, the pseudo-region is discovered, created, and cached so that following accesses succeeds.

There are a lot of ways to fix this issue, I'm not sure which is best. I'm starting to think whenever we access an object (say "top"), we should be iterating over all related objects (say "genblk1[0]"), properly creating and caching those related-objects. This approach is also the best solution for #1128.
@ktbarrett It might be enough to call `_discover_all` before looking for sub handles.
```diff
--- a/cocotb/handle.py
+++ b/cocotb/handle.py
@@ -249,6 +249,11 @@ class HierarchyObject(RegionObject):
         except KeyError:
             pass
 
+        if not self._discovered:
+            self._discover_all()
+            return self.__get_sub_handle_by_name(name)
+
         # Cache to avoid a call to the simulator if we already know the name is
         # invalid. Unclear if we care, but we had this before.
         if name in self._invalid_sub_handles:
```

This resolves the problem. Could someone make a PR (with a newsfragment)?

Resolving patch:
diff --git a/cocotb/handle.py b/cocotb/handle.py
--- a/cocotb/handle.py
+++ b/cocotb/handle.py
@@ -249,6 +249,10 @@ def __get_sub_handle_by_name(self, name):
         except KeyError:
             pass
 
+        if not self._discovered:
+            self._discover_all()
+            return self.__get_sub_handle_by_name(name)
+
         # Cache to avoid a call to the simulator if we already know the name is
         # invalid. Unclear if we care, but we had this before.
         if name in self._invalid_sub_handles:


Test patch:
diff --git a/tests/designs/sample_module/sample_module.sv b/tests/designs/sample_module/sample_module.sv
--- a/tests/designs/sample_module/sample_module.sv
+++ b/tests/designs/sample_module/sample_module.sv
@@ -98,8 +98,8 @@ initial begin
     $dumpvars(0,sample_module);
 end
 
-reg[3:0] temp;
 parameter NUM_OF_MODULES = 4;
+reg[NUM_OF_MODULES-1:0] temp;
 genvar idx;
 generate
     for (idx = 0; idx < NUM_OF_MODULES; idx=idx+1) begin
diff --git a/tests/designs/sample_module/sample_module.vhdl b/tests/designs/sample_module/sample_module.vhdl
--- a/tests/designs/sample_module/sample_module.vhdl
+++ b/tests/designs/sample_module/sample_module.vhdl
@@ -107,8 +107,20 @@ architecture impl of sample_module is
     type twoDimArrayType is array (natural range <>) of unsignedArrayType(31 downto 28);
     signal array_2d         : twoDimArrayType(0 to 1);
 
+    constant NUM_OF_MODULES : natural := 4;
+    signal temp             : std_logic_vector(NUM_OF_MODULES-1 downto 0);
+
 begin
 
+    genblk1: for i in NUM_OF_MODULES - 1 downto 0 generate
+    begin
+        process (clk) begin
+            if rising_edge(clk) then
+                temp(i) <= '0';
+            end if;
+        end process;
+    end generate;
+
     process (clk) begin
         if rising_edge(clk) then
             stream_out_data_registered <= stream_in_data;
diff --git a/tests/test_cases/test_array/test_array.py b/tests/test_cases/test_array/test_array.py
--- a/tests/test_cases/test_array/test_array.py
+++ b/tests/test_cases/test_array/test_array.py
@@ -336,7 +336,7 @@ def test_discover_all(dut):
     # Modelsim/Questa VPI will not find a vpiStructVar from vpiModule so we set a dummy variable
     # to ensure the handle is in the dut "sub_handles" for iterating
     #
-    # DO NOT ADD FOR ALDEC.  Does not iterate over properly
+    # DO NOT ADD FOR ALDEC.  Older Versions do not iterate over properly
     if cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("modelsim", "ncsim", "xmsim")):
         dummy = dut.sig_rec
         dummy = dut.port_rec_out
@@ -353,12 +353,16 @@ def test_discover_all(dut):
     elif cocotb.LANGUAGE in ["vhdl"]:
         pass_total = 856
     elif cocotb.LANGUAGE in ["verilog"] and cocotb.SIM_NAME.lower().startswith(("riviera")):
+        # Numbers for versions before 2019.10 may be outdated
         if cocotb.SIM_VERSION.startswith(("2017.10.61")):
             pass_total = 803
         elif cocotb.SIM_VERSION.startswith(("2016.06", "2016.10", "2017.02")):
             pass_total = 813
-        elif cocotb.SIM_VERSION.startswith(("2016.02", "2019.10")):
+        elif cocotb.SIM_VERSION.startswith(("2016.02")):
             pass_total = 947
+        elif cocotb.SIM_VERSION.startswith(("2019.10")):
+            # vpiVariables finds port_rec_out and sig_rec
+            pass_total = 1006
         else:
             pass_total = 1038
     else:
diff --git a/tests/test_cases/test_discovery/test_discovery.py b/tests/test_cases/test_discovery/test_discovery.py
--- a/tests/test_cases/test_discovery/test_discovery.py
+++ b/tests/test_cases/test_discovery/test_discovery.py
@@ -34,6 +34,20 @@
 from cocotb.handle import IntegerObject, ConstantObject, HierarchyObject, StringObject
 
 
+@cocotb.test()
+async def pseudo_region_access(dut):
+    """Test that pseudo-regions are accessible before iteration"""
+
+    # Ensure pseudo-region lookup will fail
+    if len(dut._sub_handles) != 0:
+        dut._sub_handles = {}
+
+    pseudo_region = dut.genblk1
+    dut._log.info("Found %s (%s)", pseudo_region._name, type(pseudo_region))
+    first_generate_instance = pseudo_region[0]
+    dut._log.info("Found %s (%s)", first_generate_instance._name, type(first_generate_instance))
+
+
 @cocotb.test()
 def recursive_discover(dut):
     """Discover absolutely everything in the DUT"""
diff --git a/tests/test_cases/test_iteration_mixedlang/test_iteration.py b/tests/test_cases/test_iteration_mixedlang/test_iteration.py
--- a/tests/test_cases/test_iteration_mixedlang/test_iteration.py
+++ b/tests/test_cases/test_iteration_mixedlang/test_iteration.py
@@ -71,7 +71,7 @@ def recursive_discovery(dut):
     elif cocotb.SIM_NAME.lower().startswith(("modelsim")):
         pass_total = 933
     else:
-        pass_total = 966
+        pass_total = 1024
 
     tlog = logging.getLogger("cocotb.test")
     yield Timer(100)