From 6e37279853b84a4c2bc0ace2e5f56daeecdab460 Mon Sep 17 00:00:00 2001
From: Rupert Swarbrick <rswarbrick@lowrisc.org>
Date: Wed, 12 Jul 2023 15:43:16 +0100
Subject: [PATCH] [reggen] Make reggen library MyPy-clean

Fix some mypy errors in the reggen library. It looks like the version
of MyPy that we're using in CI at the moment isn't new enough to
notice the problems, so we won't really see much change from this
commit. However, these changes fix things with 1.0.1 (the latest
release), which spat out lots of messages without them.

Getting things happy again is mostly a case of adding some types to
guide the tool so that it knows what's going on. There's also a small
re-write in gen_selfdoc.py: there, the doc_tbl_head() function used an
optional int as a way of encoding a bool. This seemed a bit silly (and
hard to encode!), so I've switched it to use True/False instead.

Finally, we fix a type error in register.py, where the code was
missing some parentheses and testing a function (rather than its
return value) against None.

One slightly odd thing is the change to access.py. There, the problem
was that self.value[1].name wasn't known to be a string. It turns out
that this depends on the Python version(!) and is tracked in
https://github.com/python/mypy/issues/12483. Using a do-nothing cast
with str() silences things.

Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
---
 util/design/mubi/prim_mubi.py | 12 ++++++------
 util/reggen/access.py         |  2 +-
 util/reggen/gen_json.py       |  8 ++++----
 util/reggen/gen_selfdoc.py    | 36 +++++++++++++++++++----------------
 util/reggen/register.py       |  2 +-
 5 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/util/design/mubi/prim_mubi.py b/util/design/mubi/prim_mubi.py
index 4a44b55e5511a..074691c98bb29 100755
--- a/util/design/mubi/prim_mubi.py
+++ b/util/design/mubi/prim_mubi.py
@@ -4,7 +4,7 @@
 # SPDX-License-Identifier: Apache-2.0
 r"""Converts mubi mako templates
 """
-from mako.template import Template
+from mako.template import Template  # type: ignore
 
 MUBI_PKG_TPL_PATH = "util/design/data/prim_mubi_pkg.sv.tpl"
 MUBI_CORE_TPL_PATH = "util/design/data/prim_mubi.core.tpl"
@@ -25,11 +25,11 @@
 N_MAX_NIBBLES = 4
 
 
-def is_width_valid(width):
+def is_width_valid(width: int) -> bool:
     return (width % 4 == 0) and (width // 4 <= N_MAX_NIBBLES) and (width > 0)
 
 
-def mubi_value_as_hexstr(sel: bool, width: int):
+def mubi_value_as_hexstr(sel: bool, width: int) -> str:
 
     if is_width_valid(width):
         nibble = int(width / 4)
@@ -48,15 +48,15 @@ def mubi_value_as_hexstr(sel: bool, width: int):
     return true_val if sel else false_val
 
 
-def mubi_value_as_int(sel: bool, width: int):
+def mubi_value_as_int(sel: bool, width: int) -> int:
     return int(mubi_value_as_hexstr(sel, width), 16)
 
 
-def get_c_path():
+def get_c_path() -> str:
     return MUBI_SW_OUT_PATH
 
 
-def gen():
+def gen() -> None:
 
     tpls = [
         (MUBI_PKG_TPL_PATH, MUBI_PKG_OUT_PATH),
diff --git a/util/reggen/access.py b/util/reggen/access.py
index 23de055dd2b44..699dcfb5052de 100644
--- a/util/reggen/access.py
+++ b/util/reggen/access.py
@@ -90,7 +90,7 @@ def dv_rights(self) -> str:
         '''Return a UVM access string as used by uvm_field::set_access().'''
         if self.key == 'r0w1c':
             return 'W1C'
-        return self.value[1].name
+        return str(self.value[1].name)
 
     def swrd(self) -> SwRdAccess:
         return self.value[3]
diff --git a/util/reggen/gen_json.py b/util/reggen/gen_json.py
index c593cc1ca15eb..8d6786aa3b6c7 100644
--- a/util/reggen/gen_json.py
+++ b/util/reggen/gen_json.py
@@ -4,10 +4,12 @@
 """Generate JSON/compact JSON/Hjson from register JSON tree
 """
 
-import hjson
+from typing import Any, TextIO
 
+import hjson  # type: ignore
 
-def gen_json(obj, outfile, format):
+
+def gen_json(obj: Any, outfile: TextIO, format: str) -> None:
     if format == 'json':
         hjson.dumpJSON(obj,
                        outfile,
@@ -30,5 +32,3 @@ def gen_json(obj, outfile, format):
                    use_decimal=True)
     else:
         raise ValueError('Invalid JSON format ' + format)
-
-    return 0
diff --git a/util/reggen/gen_selfdoc.py b/util/reggen/gen_selfdoc.py
index adbef66795254..a1345cb9fcb83 100644
--- a/util/reggen/gen_selfdoc.py
+++ b/util/reggen/gen_selfdoc.py
@@ -10,8 +10,10 @@
                     ip_block, enum_entry, field,
                     register, multi_register, window)
 
+from typing import Any, Optional, TextIO
 
-def genout(outfile, msg):
+
+def genout(outfile: TextIO, msg: str) -> None:
     outfile.write(msg)
 
 
@@ -200,8 +202,8 @@ def genout(outfile, msg):
 """
 
 
-def doc_tbl_head(outfile, use):
-    if use is not None:
+def doc_tbl_head(outfile: TextIO, use: bool) -> None:
+    if use:
         genout(outfile, "\nKey | Kind | Type | Description of Value\n")
         genout(outfile, "--- | ---- | ---- | --------------------\n")
     else:
@@ -209,7 +211,8 @@ def doc_tbl_head(outfile, use):
         genout(outfile, "--- | -----------\n")
 
 
-def doc_tbl_line(outfile, key, use, desc):
+def doc_tbl_line(outfile: TextIO, key: str,
+                 use: Optional[str], desc: Any) -> None:
     if use is not None:
         desc_key, desc_txt = desc
         val_type = (validate.val_types[desc_key][0]
@@ -220,6 +223,7 @@ def doc_tbl_line(outfile, key, use, desc):
         desc_txt = desc
 
     if val_type is not None:
+        assert use is not None
         genout(
             outfile, '{} | {} | {} | {}\n'.format(key, validate.key_use[use],
                                                   val_type, desc_txt))
@@ -227,7 +231,7 @@ def doc_tbl_line(outfile, key, use, desc):
         genout(outfile, key + " | " + desc_txt + "\n")
 
 
-def document(outfile):
+def document(outfile: TextIO) -> None:
     genout(outfile, doc_intro)
     for x in validate.val_types:
         genout(
@@ -235,19 +239,19 @@ def document(outfile):
             validate.val_types[x][0] + " | " + validate.val_types[x][1] + "\n")
 
     genout(outfile, swaccess_intro)
-    doc_tbl_head(outfile, None)
+    doc_tbl_head(outfile, False)
     for key, value in SWACCESS_PERMITTED.items():
         doc_tbl_line(outfile, key, None, value[0])
 
     genout(outfile, hwaccess_intro)
-    doc_tbl_head(outfile, None)
-    for key, value in HWACCESS_PERMITTED.items():
-        doc_tbl_line(outfile, key, None, value[0])
+    doc_tbl_head(outfile, False)
+    for key, hw_value in HWACCESS_PERMITTED.items():
+        doc_tbl_line(outfile, key, None, hw_value[0])
 
     genout(
         outfile, "\n\nThe top level of the JSON is a group containing "
         "the following keys:\n")
-    doc_tbl_head(outfile, 1)
+    doc_tbl_head(outfile, True)
     for k, v in ip_block.REQUIRED_FIELDS.items():
         doc_tbl_line(outfile, k, 'r', v)
     for k, v in ip_block.OPTIONAL_FIELDS.items():
@@ -257,7 +261,7 @@ def document(outfile):
     genout(
         outfile, "\n\nThe list of registers includes register definition "
         "groups containing the following keys:\n")
-    doc_tbl_head(outfile, 1)
+    doc_tbl_head(outfile, True)
     for k, v in register.REQUIRED_FIELDS.items():
         doc_tbl_line(outfile, k, 'r', v)
     for k, v in register.OPTIONAL_FIELDS.items():
@@ -267,7 +271,7 @@ def document(outfile):
     genout(
         outfile, "\n\nIn the fields list each field definition is a group "
         "itself containing the following keys:\n")
-    doc_tbl_head(outfile, 1)
+    doc_tbl_head(outfile, True)
     for k, v in field.REQUIRED_FIELDS.items():
         doc_tbl_line(outfile, k, 'r', v)
     for k, v in field.OPTIONAL_FIELDS.items():
@@ -275,14 +279,14 @@ def document(outfile):
     genout(outfile, field_example)
 
     genout(outfile, "\n\nDefinitions in an enumeration group contain:\n")
-    doc_tbl_head(outfile, 1)
+    doc_tbl_head(outfile, True)
     for k, v in enum_entry.REQUIRED_FIELDS.items():
         doc_tbl_line(outfile, k, 'r', v)
 
     genout(
         outfile, "\n\nThe list of registers may include single entry groups "
         "to control the offset, open a window or generate registers:\n")
-    doc_tbl_head(outfile, 1)
+    doc_tbl_head(outfile, True)
     for x in validate.list_optone:
         doc_tbl_line(outfile, x, 'o', validate.list_optone[x])
 
@@ -290,14 +294,14 @@ def document(outfile):
     genout(outfile, regwen_intro)
 
     genout(outfile, window_intro)
-    doc_tbl_head(outfile, 1)
+    doc_tbl_head(outfile, True)
     for k, v in window.REQUIRED_FIELDS.items():
         doc_tbl_line(outfile, k, 'r', v)
     for k, v in window.OPTIONAL_FIELDS.items():
         doc_tbl_line(outfile, k, 'o', v)
 
     genout(outfile, multi_intro)
-    doc_tbl_head(outfile, 1)
+    doc_tbl_head(outfile, True)
     for k, v in multi_register.REQUIRED_FIELDS.items():
         doc_tbl_line(outfile, k, 'r', v)
     for k, v in multi_register.OPTIONAL_FIELDS.items():
diff --git a/util/reggen/register.py b/util/reggen/register.py
index cfc5c39f24e0a..b2def748166c7 100644
--- a/util/reggen/register.py
+++ b/util/reggen/register.py
@@ -200,7 +200,7 @@ def __init__(self,
         for field in self.fields:
             if field.swaccess.key == 'rc':
                 rc_fields.append(field.name)
-            elif field.swaccess.allows_write:
+            elif field.swaccess.allows_write():
                 we_fields.append(field.name)
         if rc_fields and we_fields:
             raise ValueError("Register {} has both software writable fields "