[reggen] Add Parameter list to the hjson format

- add `param_list` in IP configuration
- The parameter name can be used in `multireg["count"]` field
- `param_list` is added to the register package

This is related to #30 #47
diff --git a/hw/ip/rv_timer/doc/rv_timer.hjson b/hw/ip/rv_timer/doc/rv_timer.hjson
index 1fcbb87..ff6f703 100644
--- a/hw/ip/rv_timer/doc/rv_timer.hjson
+++ b/hw/ip/rv_timer/doc/rv_timer.hjson
@@ -15,13 +15,25 @@
       desc: "raised if the timer 0_0 expired (mtimecmp >= mtime)"
     },
   ],
+  param_list: [
+    { name: "N_HARTS",
+      desc: "Number of harts",
+      type: "int",
+      default: "1"
+    },
+    { name: "N_TIMERS",
+      desc: "Number of timers per Hart",
+      type: "int",
+      default: "1"
+    }
+  ],
   no_auto_intr_regs: "true",
   regwidth: "32",
   registers: [
     { multireg: {
         name: "CTRL",
         desc: "Control register",
-        count: 1,
+        count: "N_HARTS",
         cname: "TIMER",
         swaccess: "rw",
         hwaccess: "hro",
@@ -75,7 +87,7 @@
     { multireg: {
         name: "INTR_ENABLE0",
         desc: "Interrupt Enable",
-        count: 1,
+        count: "N_TIMERS",
         cname: "TIMER",
         swaccess: "rw",
         hwaccess: "hro",
@@ -87,7 +99,7 @@
     { multireg: {
         name: "INTR_STATE0",
         desc: "Interrupt Status",
-        count: 1,
+        count: "N_TIMERS",
         cname: "TIMER",
         swaccess: "rw1c",
         hwaccess: "hrw",
@@ -99,7 +111,7 @@
     { multireg: {
         name: "INTR_TEST0",
         desc: "Interrupt test register",
-        count: 1,
+        count: "N_TIMERS",
         cname: "TIMER",
         swaccess: "wo",
         hwaccess: "hro",
diff --git a/hw/ip/rv_timer/doc/rv_timer.tpl.hjson b/hw/ip/rv_timer/doc/rv_timer.tpl.hjson
index 8b652cf..fa07de8 100644
--- a/hw/ip/rv_timer/doc/rv_timer.tpl.hjson
+++ b/hw/ip/rv_timer/doc/rv_timer.tpl.hjson
@@ -22,13 +22,25 @@
 % endfor
 % endfor
   ],
+  param_list: [
+    { name: "N_HARTS",
+      desc: "Number of harts",
+      type: "int",
+      default: "${harts}"
+    },
+    { name: "N_TIMERS",
+      desc: "Number of timers per Hart",
+      type: "int",
+      default: "${timers}"
+    }
+  ],
   no_auto_intr_regs: "true",
   regwidth: "32",
   registers: [
     { multireg: {
         name: "CTRL",
         desc: "Control register",
-        count: ${harts},
+        count: "N_HARTS",
         cname: "TIMER",
         swaccess: "rw",
         hwaccess: "hro",
@@ -85,7 +97,7 @@
     { multireg: {
         name: "INTR_ENABLE${i}",
         desc: "Interrupt Enable",
-        count: ${timers},
+        count: "N_TIMERS",
         cname: "TIMER",
         swaccess: "rw",
         hwaccess: "hro",
@@ -97,7 +109,7 @@
     { multireg: {
         name: "INTR_STATE${i}",
         desc: "Interrupt Status",
-        count: ${timers},
+        count: "N_TIMERS",
         cname: "TIMER",
         swaccess: "rw1c",
         hwaccess: "hrw",
@@ -109,7 +121,7 @@
     { multireg: {
         name: "INTR_TEST${i}",
         desc: "Interrupt test register",
-        count: ${timers},
+        count: "N_TIMERS",
         cname: "TIMER",
         swaccess: "wo",
         hwaccess: "hro",
diff --git a/hw/ip/rv_timer/rtl/rv_timer_reg_pkg.sv b/hw/ip/rv_timer/rtl/rv_timer_reg_pkg.sv
index 9fba0ba..30c80d7 100644
--- a/hw/ip/rv_timer/rtl/rv_timer_reg_pkg.sv
+++ b/hw/ip/rv_timer/rtl/rv_timer_reg_pkg.sv
@@ -6,6 +6,10 @@
 
 package rv_timer_reg_pkg;
 
+  // Param list
+  localparam int N_HARTS = 1;
+  localparam int N_TIMERS = 1;
+
 // Register to internal design logic
 typedef struct packed {
 
diff --git a/util/reggen/gen_rtl.py b/util/reggen/gen_rtl.py
index 9e2ce58..8d1933c 100644
--- a/util/reggen/gen_rtl.py
+++ b/util/reggen/gen_rtl.py
@@ -89,6 +89,7 @@
     regs = []
     wins = []
     blocks = []
+    params = []
 
     def __init__(self):
         self.width = 32
@@ -98,6 +99,7 @@
         self.regs = []
         self.wins = []
         self.blocks = []
+        self.params = []
 
 
 def escape_name(name):
@@ -208,6 +210,8 @@
 
     log.info("Data Width is set to %d bits", block.width)
 
+    block.params = obj["param_list"] if "param_list" in obj else []
+
     for r in obj["registers"]:
         # Check if any exception condition hit
         if 'reserved' in r:
diff --git a/util/reggen/reg_pkg.tpl.sv b/util/reggen/reg_pkg.tpl.sv
index 3a0cb6a..cb9df67 100644
--- a/util/reggen/reg_pkg.tpl.sv
+++ b/util/reggen/reg_pkg.tpl.sv
@@ -9,6 +9,13 @@
   max_regs_char = len("{}".format(num_regs-1))
 %>\
 package ${block.name}_reg_pkg;
+% if len(block.params) != 0:
+
+  // Param list
+% endif
+% for param in block.params:
+  localparam ${param["type"]} ${param["name"]} = ${param["default"]};
+% endfor
 
 // Register to internal design logic
 typedef struct packed {
diff --git a/util/reggen/validate.py b/util/reggen/validate.py
index da45c21..2793f39 100644
--- a/util/reggen/validate.py
+++ b/util/reggen/validate.py
@@ -27,6 +27,18 @@
     return d
 
 
+def check_count(params, x, err_prefix):
+    '''Checking mreg count if it is in param list
+    '''
+    name_list = [z["name"] for z in params]
+    try:
+        index = name_list.index(x)
+        return check_int(params[index]["default"], err_prefix + " default")
+    except ValueError:
+        # cannot find entry in the param list
+        return check_int(x, err_prefix)
+
+
 # validating version of int(x, 0)
 # returns int value, error flag
 # if error flag is True value will be zero
@@ -92,6 +104,31 @@
     return error
 
 
+def check_lp(obj, x, err_prefix):
+    error = 0
+    if not isinstance(obj[x], list):
+        log.error(err_prefix + ' element ' + x + ' not a list')
+        return 1
+
+    for y in obj[x]:
+        error += check_keys(y, lp_required, lp_optional, {},
+                            err_prefix + ' element ' + x)
+        # TODO: Check if PascalCase or ALL_CAPS
+        if not "type" in y:
+            y["type"] = "int"
+        if not "default" in y:
+            if y["type"][:3] == "int":
+                y["default"] = "1"
+            elif y["type"] == "string":
+                y["default"] = ""
+            else:
+                log.err(err_prefix + ' element ' + x + '["' + y["name"] +
+                        '"]' + ' type is not supported')
+                return error + 1
+
+    return error
+
+
 def check_keys(obj, required_keys, optional_keys, added_keys, err_prefix):
     error = 0
     for x in required_keys:
@@ -108,6 +145,9 @@
             log.warning(err_prefix + " contains extra key " + x)
         if type[:2] == 'ln':
             error += check_ln(obj, x, type == 'lnw', err_prefix)
+        if type == 'lp':
+            error += check_lp(obj, x, err_prefix)
+
     return error
 
 
@@ -179,6 +219,7 @@
            'one or more groups that have just name and dscr keys.'\
            ' e.g. `{ name: "name", desc: "description"}`'],
     'lnw': ["name list+", 'name list that optionally contains a width'],
+    'lp': ["parameter list", 'parameter list having default value optionally'],
     'g': ["group", "comma separated group of key:value enclosed in `{}`"],
     's': ["string", "string, typically short"],
     't': ["text", "string, may be multi-line enclosed in `'''` "\
@@ -212,6 +253,7 @@
                           "Defaults to false if not present."],
     'alert_list': ['ln', "list of peripheral alerts"],
     'regwidth': ['d', "width of registers in bits (default 32)"],
+    'param_list': ['lp', "list of parameters of the IP"],
     'SPDX-License-Identifier': ['s', "License ientifier (if using pure json) "\
                                 "Only use this if unable to put this "\
                                 "information in a comment at the top of the "\
@@ -235,6 +277,16 @@
     'width': ['d', "bit width of the item (if not 1)"],
 }
 
+# lp type
+lp_required = {
+    'name': ['s', "name of the item"],
+}
+lp_optional = {
+    'desc': ['s', "description of the item"],
+    'type': ['s', "item type. int by default"],
+    'default': ['s', "item default value"],
+}
+
 # Registers list may have embedded keys
 list_optone = {'reserved': ['d', "number of registers to reserve space for"],
               'skipto':    ['d', "set next register offset to value"],
@@ -329,7 +381,9 @@
 # Multireg keys
 multireg_required = {'name':   ['s', "base name of the registers"],
                      'desc':   ['t', "description of the registers"],
-                     'count':  ['d', "number of instances to generate"],
+                     'count':  ['s', "number of instances to generate."\
+                                " This field can be integer or string matching"\
+                                " from param_list"],
                      'cname':  ['s', "base name for each instance, mostly "\
                                 "useful for refering to instance in messages"],
                      'fields': ['l', "list of register field description"\
@@ -773,8 +827,7 @@
     reg['genoffset'] = offset
     reg['gendvrights'] = parse_dvrights(default_sw)
 
-    if ((reg['regwen'] != '') and
-        (not reg['regwen'] in top['genwennames'])):
+    if ((reg['regwen'] != '') and (not reg['regwen'] in top['genwennames'])):
         top['genwennames'].append(reg['regwen'])
 
     log.info(rname + "@" + hex(offset) + " " + str(error) + " errors. Mask " +
@@ -809,7 +862,10 @@
 
     error += gen[0]
 
-    mcount, ierr = check_int(mreg['count'], mrname + " multireg count")
+    # Check `count` field if it is in paramter list
+    mcount, ierr = check_count(
+        top['param_list'] if "param_list" in top else [], mreg['count'],
+        mrname + " multireg count")
     if ierr:
         error += 1
 
@@ -1119,8 +1175,8 @@
     # auto header generation would go here and update autoregs
 
     if 'no_auto_intr_regs' in regs:
-        no_autoi, err = check_bool(
-            regs['no_auto_intr_regs'], 'no_auto_intr_regs')
+        no_autoi, err = check_bool(regs['no_auto_intr_regs'],
+                                   'no_auto_intr_regs')
         if err:
             error += 1
     else: