[topgen] Refactor Inter-module to support all

This commit is to support most comb. of inter-module format.

Changes are:

- Remove concept of broadcast/giver in ip.hjson
  + type field is now one of ['req_rsp', 'uni']
  + ip only defines the type and width. broadcast or giver is determined
    in the top based on the connection.
    For example, if width 1 struct connects to multiple IPs, it is
    broadcast type.
- Change the act to shorter name ['req', 'rsp', 'rcv']
- Template:
  + Remove conditions in the definitions in template
- Revise `inter_module` data structure
  + 'connect' field: indicate the connection between modules
  + 'top' field: Connect to top net. It creates the definition too
  + 'ext' field: Not yet implemented. Purpose is to create ports in top
    module definition

Signed-off-by: Eunchan Kim <eunchan@opentitan.org>
diff --git a/hw/ip/flash_ctrl/data/flash_ctrl.hjson b/hw/ip/flash_ctrl/data/flash_ctrl.hjson
index 388fd5e..f2fc062 100644
--- a/hw/ip/flash_ctrl/data/flash_ctrl.hjson
+++ b/hw/ip/flash_ctrl/data/flash_ctrl.hjson
@@ -18,7 +18,7 @@
     { struct:  "flash",          // flash_req_t, flash_rsp_t
       type:    "req_rsp",
       name:    "flash",          // flash_o (req), flash_i (rsp)
-      act:     "requester",
+      act:     "req",
       package: "flash_ctrl_pkg", // Origin package (only needs for the requester)
     }
   ],
diff --git a/hw/ip/pwrmgr/data/pwrmgr.hjson b/hw/ip/pwrmgr/data/pwrmgr.hjson
index 363193b..86a34dd 100644
--- a/hw/ip/pwrmgr/data/pwrmgr.hjson
+++ b/hw/ip/pwrmgr/data/pwrmgr.hjson
@@ -16,49 +16,49 @@
     { struct:  "pwr_ast",
       type:    "req_rsp",
       name:    "pwr_ast",
-      act:     "requester",
+      act:     "req",
       package: "pwrmgr_pkg",
     },
 
     { struct:  "pwr_rst",
       type:    "req_rsp",
       name:    "pwr_rst",
-      act:     "requester",
+      act:     "req",
       package: "pwrmgr_pkg",
     },
 
     { struct:  "pwr_clk",
       type:    "req_rsp",
       name:    "pwr_clk",
-      act:     "requester",
+      act:     "req",
       package: "pwrmgr_pkg",
     },
 
     { struct:  "pwr_otp",
       type:    "req_rsp",
       name:    "pwr_otp",
-      act:     "requester",
+      act:     "req",
       package: "pwrmgr_pkg",
     },
 
     { struct:  "pwr_lc",
       type:    "req_rsp",
       name:    "pwr_lc",
-      act:     "requester",
+      act:     "req",
       package: "pwrmgr_pkg",
     },
 
     { struct:  "pwr_pinmux",
-      type:    "broadcast",
+      type:    "uni",
       name:    "pwr_pinmux",
-      act:     "receiver",
+      act:     "rcv",
       package: "pwrmgr_pkg",
     },
 
     { struct:  "pwr_peris",
-      type:    "broadcast",
+      type:    "uni",
       name:    "pwr_peris",
-      act:     "receiver",
+      act:     "rcv",
       package: "pwrmgr_pkg",
     },
 
diff --git a/hw/ip/rstmgr/data/rstmgr.hjson b/hw/ip/rstmgr/data/rstmgr.hjson
index 32c7721..444d80e 100644
--- a/hw/ip/rstmgr/data/rstmgr.hjson
+++ b/hw/ip/rstmgr/data/rstmgr.hjson
@@ -24,36 +24,36 @@
     { struct:  "pwr_rst",    // rstmgr_req_t, rstmgr_rsp_t
       type:    "req_rsp",
       name:    "pwr",        // resets_o (req), resets_i (rsp)
-      act:     "responder",
-      package: "rstmgr_pkg", // Origin package (only needs for the requester)
+      act:     "rsp",
+      package: "rstmgr_pkg", // Origin package (only needs for the req)
     },
 
     { struct:  "rstmgr_out",
-      type:    "broadcast",
+      type:    "uni",
       name:    "rstmgr",
-      act:     "requester",
-      package: "rstmgr_pkg", // Origin package (only needs for the requester)
+      act:     "req",
+      package: "rstmgr_pkg", // Origin package (only needs for the req)
     },
 
     { struct:  "rstmgr_ast",
-      type:    "broadcast",
+      type:    "uni",
       name:    "ast",
-      act:     "receiver",
-      package: "rstmgr_pkg", // Origin package (only needs for the requester)
+      act:     "rcv",
+      package: "rstmgr_pkg", // Origin package (only needs for the req)
     },
 
     { struct:  "rstmgr_cpu",
-      type:    "broadcast",
+      type:    "uni",
       name:    "cpu",
-      act:     "receiver",
-      package: "rstmgr_pkg", // Origin package (only needs for the requester)
+      act:     "rcv",
+      package: "rstmgr_pkg", // Origin package (only needs for the req)
     },
 
     { struct:  "rstmgr_peri",
-      type:    "broadcast",
+      type:    "uni",
       name:    "peri",
-      act:     "receiver",
-      package: "rstmgr_pkg", // Origin package (only needs for the requester)
+      act:     "rcv",
+      package: "rstmgr_pkg", // Origin package (only needs for the req)
     }
   ],
 
diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
index 762f053..2abd6af 100644
--- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
+++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
@@ -313,7 +313,7 @@
           struct: flash
           type: req_rsp
           name: flash
-          act: requester
+          act: req
           package: flash_ctrl_pkg
           inst_name: flash_ctrl
           width: 1
@@ -764,9 +764,10 @@
           struct: flash
           type: req_rsp
           name: flash_ctrl
-          act: responder
+          act: rsp
           inst_name: eflash
           width: 1
+          package: flash_ctrl_pkg
           top_signame: flash_ctrl_flash
           index: -1
         }
@@ -775,10 +776,15 @@
   ]
   inter_module:
   {
-    flash_ctrl.flash:
-    [
-      eflash.flash_ctrl
-    ]
+    connect:
+    {
+      flash_ctrl.flash:
+      [
+        eflash.flash_ctrl
+      ]
+    }
+    top: []
+    ext: []
   }
   xbar:
   [
@@ -1746,7 +1752,7 @@
         struct: flash
         type: req_rsp
         name: flash
-        act: requester
+        act: req
         package: flash_ctrl_pkg
         inst_name: flash_ctrl
         width: 1
@@ -1757,9 +1763,10 @@
         struct: flash
         type: req_rsp
         name: flash_ctrl
-        act: responder
+        act: rsp
         inst_name: eflash
         width: 1
+        package: flash_ctrl_pkg
         top_signame: flash_ctrl_flash
         index: -1
       }
@@ -1768,8 +1775,15 @@
     [
       {
         package: flash_ctrl_pkg
-        struct: flash
-        signame: flash_ctrl_flash
+        struct: flash_req
+        signame: flash_ctrl_flash_req
+        width: 1
+        type: req_rsp
+      }
+      {
+        package: flash_ctrl_pkg
+        struct: flash_rsp
+        signame: flash_ctrl_flash_rsp
         width: 1
         type: req_rsp
       }
diff --git a/hw/top_earlgrey/data/top_earlgrey.hjson b/hw/top_earlgrey/data/top_earlgrey.hjson
index 2b4e447..6b4e813 100644
--- a/hw/top_earlgrey/data/top_earlgrey.hjson
+++ b/hw/top_earlgrey/data/top_earlgrey.hjson
@@ -167,7 +167,7 @@
         { struct: "flash",    // flash_req_t, flash_rsp_t
           type: "req_rsp",
           name: "flash_ctrl", // flash_ctrl_i (req), flash_ctrl_o (rsp)
-          act:  "responder",
+          act:  "rsp",
         }
       ],
     },
@@ -177,10 +177,20 @@
   // format:
   //    requester: [ resp1, resp2, ... ],
   //
-  //  the field and value should be module_inst.signal_name
+  //  the field and value should be module_inst.port_name
   //  e.g flash_ctrl0.flash: [flash_phy0.flash_ctrl]
   inter_module: {
-    'flash_ctrl.flash': ['eflash.flash_ctrl']
+    'connect': {
+      'flash_ctrl.flash': ['eflash.flash_ctrl']
+    }
+
+    // top is to connect to top net/struct.
+    // It defines the signal in the top and connect from the module,
+    // use of the signal is up to top template
+    'top': [],
+
+    // ext is to create port in the top.
+    'ext': [],
   },
 
   debug_mem_base_addr: "0x1A110000",
diff --git a/hw/top_earlgrey/data/top_earlgrey.sv.tpl b/hw/top_earlgrey/data/top_earlgrey.sv.tpl
index 3087f85..d63e1db 100644
--- a/hw/top_earlgrey/data/top_earlgrey.sv.tpl
+++ b/hw/top_earlgrey/data/top_earlgrey.sv.tpl
@@ -199,6 +199,14 @@
   end
 % endif
 
+## Inter-module Definitions
+% if len(top["inter_signal"]["definitions"]) >= 1:
+  // define inter-module signals
+% endif
+% for sig in top["inter_signal"]["definitions"]:
+  ${lib.im_defname(sig)} ${lib.bitarray(sig["width"],1)} ${sig["signame"]};
+% endfor
+
   // Clock assignments
 % for clock in top['clocks']:
   % if clock['name'] != "usb" :
@@ -438,22 +446,6 @@
 
   % elif m["type"] == "eflash":
 
-  % if len(top["inter_signal"]["definitions"]) >= 1:
-  // define inter-module signals
-  % endif
-  % for sig in top["inter_signal"]["definitions"]:
-    % if sig["type"] == "req_rsp":
-  ${sig["package"]}::${sig["struct"]}_req_t ${lib.bitarray(sig["width"],1)} ${sig["signame"]}_req;
-  ${sig["package"]}::${sig["struct"]}_rsp_t ${lib.bitarray(sig["width"],1)} ${sig["signame"]}_rsp;
-    % elif sig["type"] == "broadcast":
-      % if sig["struct"] == "logic":
-  logic ${lib.bitarray(sig["width"],1)} ${sig["signame"]};
-      % else:
-  ${sig["package"]}::${sig["struct"]}_t ${sig["signame"]};
-      % endif
-    % endif
-  % endfor
-
   // host to flash communication
   logic flash_host_req;
   logic flash_host_req_rdy;
@@ -588,40 +580,13 @@
       // Inter-module signals
       % for sig in m["inter_signal_list"]:
         ## TODO: handle below condition in lib.py
-        % if "top_signame" in sig:
-          % if sig["type"] == "req_rsp":
-            % if sig["act"] == "requester":
-      .${sig["name"]}_o(${sig["top_signame"]}_req${lib.index(sig["index"])}),
-      .${sig["name"]}_i(${sig["top_signame"]}_rsp${lib.index(sig["index"])}),
-          % elif sig["act"] == "responder":
-      .${sig["name"]}_i(${sig["top_signame"]}_req${lib.index(sig["index"])}),
-      .${sig["name"]}_o(${sig["top_signame"]}_rsp${lib.index(sig["index"])}),
-            % endif # sig["act"] == requester
-          % elif sig["type"] == "broadcast":
-            ## TODO: Broadcast type
-            % if sig["act"] == "requester":
-      .${sig["name"]}_o(${sig["top_signame"]}${lib.index(sig["index"])}),
-            % elif sig["act"] == "receiver":
-      .${sig["name"]}_i(${sig["top_signame"]}${lib.index(sig["index"])}),
-            % endif
-          % endif
-        % else: # no top_signame in sig
-          % if sig["type"] == "req_rsp":
-            % if sig["act"] == "requester":
-      .${sig["name"]}_o(),
-      .${sig["name"]}_i(${sig["package"]}::${sig["struct"].upper()}_RSP_DEFAULT),
-            % elif sig["act"] == "responder":
-      .${sig["name"]}_i(${sig["package"]}::${sig["struct"].upper()}_REQ_DEFAULT),
-      .${sig["name"]}_o(),
-            % endif
-          % elif sig["type"] == "broadcast":
-            % if sig["act"] == "requester":
-      .${sig["name"]}_o(),
-            % elif sig["act"] == "receiver":
-              ## TODO: Add logic struct default value
-      .${sig["name"]}_i(${sig["package"]}::${sig["struct"].upper()}_DEFAULT),
-            % endif
-          % endif
+        % if sig["type"] == "req_rsp":
+      .${lib.im_portname(sig,"req")}(${lib.im_netname(sig, "req")}),
+      .${lib.im_portname(sig,"rsp")}(${lib.im_netname(sig, "rsp")}),
+        % elif sig["type"] == "uni":
+          ## TODO: Broadcast type
+          ## TODO: default for logic type
+      .${lib.im_portname(sig)}(${lib.im_netname(sig)}),
         % endif
       % endfor
     % endif
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
index 6f77f62..94acbed 100644
--- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
+++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
@@ -252,6 +252,10 @@
   prim_esc_pkg::esc_rx_t [alert_pkg::N_ESC_SEV-1:0]  esc_rx;
 
 
+  // define inter-module signals
+  flash_ctrl_pkg::flash_req_t       flash_ctrl_flash_req;
+  flash_ctrl_pkg::flash_rsp_t       flash_ctrl_flash_rsp;
+
   // Clock assignments
   assign main_clk = clk_i;
   assign fixed_clk = clk_i;
@@ -444,10 +448,6 @@
     .rdata_o  (ram_main_rdata)
   );
 
-  // define inter-module signals
-  flash_ctrl_pkg::flash_req_t       flash_ctrl_flash_req;
-  flash_ctrl_pkg::flash_rsp_t       flash_ctrl_flash_rsp;
-
   // host to flash communication
   logic flash_host_req;
   logic flash_host_req_rdy;
diff --git a/util/topgen/intermodule.py b/util/topgen/intermodule.py
index 2ca2bd2..2de3b94 100644
--- a/util/topgen/intermodule.py
+++ b/util/topgen/intermodule.py
@@ -6,8 +6,31 @@
 import logging as log
 from typing import Dict, Tuple
 from collections import OrderedDict
+from enum import Enum
 
 from reggen.validate import check_int
+from topgen import lib
+
+IM_TYPES = ['uni', 'req_rsp']
+IM_ACTS = ['req', 'rsp', 'rcv']
+IM_CONN_TYPE = ['1-to-1', '1-to-N', 'broadcast']
+
+
+class ImType(Enum):
+    Uni = 1
+    ReqRsp = 2
+
+
+class ImAct(Enum):
+    Req = 1
+    Rsp = 2
+    Rcv = 3
+
+
+class ImConn(Enum):
+    OneToOne = 1  # req <-> {rsp,rcv} with same width
+    OneToN = 2  # req width N <-> N x {rsp,rcv}s width 1
+    Broadcast = 3  # req width 1 <-> N x rcvs width 1
 
 
 def intersignal_format(req: Dict) -> str:
@@ -18,8 +41,7 @@
     """
 
     # TODO: Handle array signal
-    result = "{req}_{struct}".format(req=req["inst_name"],
-                                     struct=req["struct"])
+    result = "{req}_{struct}".format(req=req["inst_name"], struct=req["name"])
 
     # check signal length if exceeds 100
 
@@ -78,7 +100,9 @@
     #
     # For example:
     #  inter_module: {
-    #    'pwr_mgr.pwrup': ['lc.pwrup', 'otp.pwrup']
+    #    'connect': {
+    #      'pwr_mgr.pwrup': ['lc.pwrup', 'otp.pwrup']
+    #    }
     #  }
     # The tool adds `struct [1:0] pwr_mgr_pwrup`
     # It doesn't matter whether `pwr_mgr.pwrup` is requester or responder.
@@ -88,13 +112,7 @@
 
     uid = 0  # Unique connection ID across the top
 
-    # inter_module: {
-    #   // template 1
-    #   'flash_ctrl.flash': ['eflash.flash_ctrl']
-    #   // template 2
-    #   'module_a.sig_a' : ['module_b.sig_a', 'module_c.sig_a[0]']
-    # },
-    for req, rsps in topcfg["inter_module"].items():
+    for req, rsps in topcfg["inter_module"]["connect"].items():
         log.info("{req} --> {rsps}".format(req=req, rsps=rsps))
 
         # Split index
@@ -124,11 +142,28 @@
                 package = ""
 
         # Add to definition
-        definitions.append(
-            OrderedDict([('package', package),
-                         ('struct', req_struct["struct"]),
-                         ('signame', sig_name), ('width', req_struct["width"]),
-                         ('type', req_struct["type"])]))
+        if req_struct["type"] == "req_rsp":
+            # Add two definitions
+            definitions.append(
+                OrderedDict([('package', package),
+                             ('struct', req_struct["struct"] + "_req"),
+                             ('signame', sig_name + "_req"),
+                             ('width', req_struct["width"]),
+                             ('type', req_struct["type"])]))
+            definitions.append(
+                OrderedDict([('package', package),
+                             ('struct', req_struct["struct"] + "_rsp"),
+                             ('signame', sig_name + "_rsp"),
+                             ('width', req_struct["width"]),
+                             ('type', req_struct["type"])]))
+        else:
+            # unidirection
+            definitions.append(
+                OrderedDict([('package', package),
+                             ('struct', req_struct["struct"]),
+                             ('signame', sig_name),
+                             ('width', req_struct["width"]),
+                             ('type', req_struct["type"])]))
 
         req_struct["index"] = -1
 
@@ -162,17 +197,63 @@
             uid += 1
 
     # TODO: Check unconnected port
+    if "top" not in topcfg["inter_module"]:
+        topcfg["inter_module"]["top"] = []
+
+    for s in topcfg["inter_module"]["top"]:
+        sig_m, sig_s, sig_i = filter_index(s)
+        assert sig_i is -1, 'top net connection should not use bit index'
+        sig = find_intermodule_signal(list_of_intersignals, sig_m, sig_s)
+        sig_name = intersignal_format(sig)
+        sig["top_signame"] = sig_name
+        if "index" not in sig:
+            sig["index"] = -1
+
+        if sig["type"] == "req_rsp":
+            # Add two definitions
+            definitions.append(
+                OrderedDict([('package', sig["package"]),
+                             ('struct', sig["struct"] + "_req"),
+                             ('signame', sig_name + "_req"),
+                             ('width', sig["width"]), ('type', sig["type"])]))
+            definitions.append(
+                OrderedDict([('package', sig["package"]),
+                             ('struct', sig["struct"] + "_rsp"),
+                             ('signame', sig_name + "_rsp"),
+                             ('width', sig["width"]), ('type', sig["type"])]))
+        else:  # if sig["type"] == "uni":
+            definitions.append(
+                OrderedDict([('package', sig["package"]),
+                             ('struct', sig["struct"]), ('signame', sig_name),
+                             ('width', sig["width"]), ('type', sig["type"])]))
+
+    if "ext" not in topcfg["inter_module"]:
+        topcfg["inter_module"]["ext"] = []
+
+    for s in topcfg["inter_module"]["ext"]:
+        sig_m, sig_s, sig_i = filter_index(s)
+        assert sig_i is -1, 'top net connection should not use bit index'
+        sig = find_intermodule_signal(list_of_intersignals, sig_m, sig_s)
+        sig_name = intersignal_format(sig)
+        sig["top_signame"] = sig_name
+        if "index" not in sig:
+            sig["index"] = -1
+        # TODO: Create top port
+
+        log.warning(
+            "Currently external signal {instname}.{signame} isn't supported. "
+            "Use it at the top manually".format(instname=sig["inst_name"],
+                                                signame=sig["name"]))
 
     for sig in topcfg["inter_signal"]["signals"]:
         # Check if it exist in definitions
         if "top_signame" in sig:
             continue
 
-        # Handle the unconnected port rule
+        # Set index to -1
+        sig["index"] = -1
 
-        # Option #1: tied the default value
-
-        # Option #2: External port (TBD)
+        # TODO: Handle the unconnected port rule
 
     if "definitions" not in topcfg["inter_signal"]:
         topcfg["inter_signal"]["definitions"] = definitions
@@ -211,13 +292,50 @@
 
 
 # Validation
+def check_intermodule_field(obj: OrderedDict, prefix: str = "") -> int:
+    error = 0
+
+    # type check
+    if obj["type"] not in IM_TYPES:
+        log.error("{prefix} Inter_signal {name} "
+                  "type {type} is incorrect.".format(prefix=prefix,
+                                                     name=obj["name"],
+                                                     type=obj["type"]))
+        error += 1
+
+    if obj["act"] not in IM_ACTS:
+        log.error("{prefix} Inter_signal {name} "
+                  "act {act} is incorrect.".format(prefix=prefix,
+                                                   name=obj["name"],
+                                                   act=obj["act"]))
+        error += 1
+
+    # Check 'width' field
+    width = 1
+    if "width" not in obj:
+        obj["width"] = 1
+    elif not isinstance(obj["width"], int):
+        width, err = check_int(obj["width"], obj["name"])
+        if err:
+            log.error("{prefix} Inter-module {inst}.{sig} 'width' "
+                      "should be int type.".format(prefix=prefix,
+                                                   inst=obj["inst_name"],
+                                                   sig=obj["name"]))
+            error += 1
+        else:
+            # convert to int value
+            obj["width"] = width
+
+    return error
+
+
 def check_intermodule(topcfg: Dict, prefix: str) -> int:
     if "inter_module" not in topcfg:
         return 0
 
     total_error = 0
 
-    for req, rsps in topcfg["inter_module"].items():
+    for req, rsps in topcfg["inter_module"]["connect"].items():
         error = 0
         # checking the key, value are in correct format
         # Allowed format
@@ -227,10 +345,12 @@
         #
         # Example:
         #   inter_module: {
-        #     'flash_ctrl.flash': ['eflash.flash_ctrl'],
-        #     'life_cycle.provision': ['debug_tap.dbg_en', 'dft_ctrl.en'],
-        #     'otp.pwr_hold': ['pwrmgr.peri[0]'],
-        #     'flash_ctrl.pwr_hold': ['pwrmgr.peri[1]'],
+        #     'connect': {
+        #       'flash_ctrl.flash': ['eflash.flash_ctrl'],
+        #       'life_cycle.provision': ['debug_tap.dbg_en', 'dft_ctrl.en'],
+        #       'otp.pwr_hold': ['pwrmgr.peri[0]'],
+        #       'flash_ctrl.pwr_hold': ['pwrmgr.peri[1]'],
+        #     }
         #   }
         #
         # If length of value list is > 1, then key should be array (width need to match)
@@ -254,20 +374,7 @@
         req_struct = find_intermodule_signal(topcfg["inter_signal"]["signals"],
                                              req_m, req_s)
 
-        # Check 'width' field
-        if "width" not in req_struct:
-            req_struct["width"] = 1
-        elif not isinstance(req_struct["width"], int):
-            width, err = check_int(req_struct["width"], req_struct["name"])
-            if err:
-                log.error(
-                    "Inter-module {inst}.{sig} 'width' should be int type.".
-                    format(inst=req_struct["inst_name"],
-                           sig=req_struct["name"]))
-                error += 1
-            else:
-                # convert to int value
-                req_struct["width"] = width
+        error += check_intermodule_field(req_struct)
 
         if req_i != -1 and len(rsps) != 1:
             # Array format should have one entry
@@ -288,24 +395,32 @@
             rsp_struct = find_intermodule_signal(
                 topcfg["inter_signal"]["signals"], rsp_m, rsp_s)
 
-            # Check 'width' field
-            width = 1
-            if "width" not in rsp_struct:
-                rsp_struct["width"] = 1
-            elif not isinstance(rsp_struct["width"], int):
-                width, err = check_int(rsp_struct["width"], rsp_struct["name"])
-                if err:
-                    log.error(
-                        "Inter-module {inst}.{sig} 'width' should be int type."
-                        .format(inst=rsp_struct["inst_name"],
-                                sig=rsp_struct["name"]))
-                    error += 1
-                else:
-                    # convert to int value
-                    rsp_struct["width"] = width
+            error += check_intermodule_field(rsp_struct)
+
+            # Type check
+            if "package" not in rsp_struct:
+                rsp_struct["package"] = req_struct["package"]
+            elif req_struct["package"] != rsp_struct["package"]:
+                log.error(
+                    "Inter-module package should be matched: "
+                    "{req}->{rsp} exp({expected}), actual({actual})".format(
+                        req=req_struct["name"],
+                        rsp=rsp_struct["name"],
+                        expected=req_struct["package"],
+                        actual=rsp_struct["package"]))
+                error += 1
+            if req_struct["type"] != rsp_struct["type"]:
+                log.error(
+                    "Inter-module type should be matched: "
+                    "{req}->{rsp} exp({expected}), actual({actual})".format(
+                        req=req_struct["name"],
+                        rsp=rsp_struct["name"],
+                        expected=req_struct["type"],
+                        actual=rsp_struct["type"]))
+                error += 1
 
             if req_struct["width"] != 1:
-                if width not in [1, req_struct["width"]]:
+                if rsp_struct["width"] not in [1, req_struct["width"]]:
                     log.error(
                         "If req {req} is an array, "
                         "rsp {rsp} shall be non-array or array with same width"
@@ -343,3 +458,61 @@
             error += 1
 
     return total_error
+
+
+# Template functions
+def im_defname(obj: OrderedDict) -> str:
+    """return definition struct name
+
+    e.g. flash_ctrl::flash_req_t
+    """
+    if obj["package"] == "":
+        # should be logic
+        return "logic"
+
+    return "{package}::{struct}_t".format(package=obj["package"],
+                                          struct=obj["struct"])
+
+
+def im_netname(obj: OrderedDict, suffix: str = "") -> str:
+    """return top signal name with index
+    """
+
+    # Floating signals
+    if "top_signame" not in obj:
+        if obj["act"] == "req" and suffix == "req":
+            return ""
+        if obj["act"] == "rsp" and suffix == "rsp":
+            return ""
+        if obj["act"] == "req" and suffix == "rsp":
+            return "{package}::{struct}_RSP_DEFAULT".format(
+                package=obj["package"], struct=obj["struct"].upper())
+        if obj["act"] == "rsp" and suffix == "req":
+            return "{package}::{struct}_REQ_DEFAULT".format(
+                package=obj["package"], struct=obj["struct"].upper())
+
+        return "FLOAT"
+
+    # Connected signals
+    assert suffix in ["", "req", "rsp"]
+
+    suffix_s = "_{suffix}".format(suffix=suffix) if suffix != "" else suffix
+    return "{top_signame}{suffix}{index}".format(
+        top_signame=obj["top_signame"],
+        suffix=suffix_s,
+        index=lib.index(obj["index"]))
+
+
+def im_portname(obj: OrderedDict, suffix: str = "") -> str:
+    """return IP's port name
+
+    e.g signame_o for requester req signal
+    """
+    if suffix == "":
+        suffix_s = "_o" if obj["act"] == "req" else "_i"
+    elif suffix == "req":
+        suffix_s = "_o" if obj["act"] == "req" else "_i"
+    else:
+        suffix_s = "_o" if obj["act"] == "rsp" else "_i"
+
+    return "{signame}{suffix}".format(signame=obj["name"], suffix=suffix_s)
diff --git a/util/topgen/lib.py b/util/topgen/lib.py
index f5d1ef9..b33d31f 100644
--- a/util/topgen/lib.py
+++ b/util/topgen/lib.py
@@ -11,6 +11,9 @@
 
 import re
 
+# Ignore flake8 warning as the function is used in the template
+from .intermodule import im_defname, im_netname, im_portname  # noqa : F401
+
 
 def is_ipcfg(ip: Path) -> bool:  # return bool
     log.info("IP Path: %s" % repr(ip))
@@ -215,6 +218,7 @@
     """
     return "[{}]".format(i) if i != -1 else ""
 
+
 def get_reset_path(resets, name):
     """Return the appropriate reset path given name
     """