[rom_ext] Add Verbose Logging to Generator

Signed-off-by: Sam Elliott <selliott@lowrisc.org>
diff --git a/util/rom-ext-manifest-generator.py b/util/rom-ext-manifest-generator.py
index fa222c5..9366625 100755
--- a/util/rom-ext-manifest-generator.py
+++ b/util/rom-ext-manifest-generator.py
@@ -36,12 +36,11 @@
 """
 
 import argparse
-from pathlib import Path
 import subprocess
+from pathlib import Path
 
 import hjson
 from mako.template import Template
-
 from topgen.c import MemoryRegion, Name
 
 DESC = """ROM_EXT manifest generator"""
@@ -60,6 +59,9 @@
     c      - C header file
     rust   - Rust module file
     format - Format description file (plaintext)
+
+  rom-ext-manifest-generator --verbose:
+    Print extra information including resulting field offsets and alignment.
 """
 
 
@@ -72,7 +74,7 @@
         return self.name + Name(["offset"])
 
 
-def generate_defines(fields):
+def generate_defines(fields, verbose=False):
     """ Generates manifest defines.
 
     This produces two lists of tuples. One with a field name and the
@@ -80,6 +82,15 @@
     description at the top for more information on the differences between these
     objects.
     """
+    def print_field_info(name, offset, size, alignment, required_alignment):
+        if verbose:
+            print("0x{:04x} - 0x{:04x}: {} (alignment: {} reqd: {})".format(
+                offset, offset + size, name, alignment, required_alignment))
+
+    def print_offset_info(name, offset, alignment, required_alignment):
+        if verbose:
+            print("       @ 0x{:04x}: {} (alignment: {} reqd: {})".format(
+                offset, name, alignment, required_alignment))
 
     base_name = Name.from_snake_case("ROM_EXT")
 
@@ -92,23 +103,46 @@
         required_alignment_bytes = required_alignment_bits // 8
 
         # The 8-byte two-step https://zinascii.com/2014/the-8-byte-two-step.html
-        # This ends up aligning `current_offset_bytes` to `required_alignment_bytes`
+        # This ends up aligning `new_offset_bytes` to `required_alignment_bytes`
         # that is greater than or equal to `current_offset_bytes`.
-        current_offset_bytes = (current_offset_bytes + required_alignment_bytes - 1) \
+        new_offset_bytes = (current_offset_bytes + required_alignment_bytes - 1) \
             & ~(required_alignment_bytes - 1)
 
+        if new_offset_bytes != current_offset_bytes and verbose:
+            print("0x{:04x} - 0x{:04x}: - (realignment) -".format(
+                current_offset_bytes, new_offset_bytes))
+
+        current_offset_bytes = new_offset_bytes
+        # This works becuase e.g. 6 is `0b0...00110` and ~(6-1) is `0b1..11010`,
+        # giving a result of `0b0...010`, or 2.
+        current_offset_alignment = current_offset_bytes \
+            & ~(current_offset_bytes - 1)
+
         if field['type'] == "offset":
             offset_name = base_name + Name.from_snake_case(field['name'])
             offset = MemoryOffset(offset_name, current_offset_bytes)
             offsets.append((field['name'], offset))
 
+            print_offset_info(field['name'], current_offset_bytes,
+                              current_offset_alignment,
+                              required_alignment_bytes)
+
         else:
             assert field['size'] % 8 == 0
             size_bytes = field['size'] // 8
             if field['type'] == "field":
                 region_name = base_name + Name.from_snake_case(field['name'])
-                region = MemoryRegion(region_name, current_offset_bytes, size_bytes)
+                region = MemoryRegion(region_name, current_offset_bytes,
+                                      size_bytes)
                 regions.append((field['name'], region))
+
+                print_field_info(field['name'], current_offset_bytes,
+                                 size_bytes, current_offset_alignment,
+                                 required_alignment_bytes)
+            elif field['type'] == 'reserved' and verbose:
+                print_field_info('- reserved -', current_offset_bytes,
+                                 size_bytes, current_offset_alignment, 0)
+
             current_offset_bytes += size_bytes
 
     return (regions, offsets)
@@ -170,14 +204,14 @@
 
     output_path = output_dir / 'manifest.txt'
 
-    truncate_length = 16 # bytes
+    truncate_length = 16  # bytes
     bits_in_byte = 8
 
     verbose_regions = []
-    current_offset = 0 # bytes
+    current_offset = 0  # bytes
 
     truncation_lines = []
-    current_truncation_delta = 0 # bytes
+    current_truncation_delta = 0  # bytes
 
     # We need to re-process this info to re-add reserved regions of the right
     # size, and also to truncate really long fields.
@@ -187,15 +221,18 @@
 
         # Pad with a reserved field to get to new offset
         if new_offset != current_offset:
-            verbose_regions.append("- reserved -:{}".format((new_offset - current_offset) * bits_in_byte))
+            verbose_regions.append("- reserved -:{}".format(
+                (new_offset - current_offset) * bits_in_byte))
 
         current_offset = new_offset
 
         # Add a (potentially truncated) field
         if mem_region.size_bytes > truncate_length:
             # We only allow truncated regions at 4-byte offsets.
-            assert(current_offset % 4 == 0)
-            verbose_regions.append("{} ({} bits):{}".format(name, mem_region.size_bytes * bits_in_byte, truncate_length * bits_in_byte))
+            assert (current_offset % 4 == 0)
+            verbose_regions.append("{} ({} bits):{}".format(
+                name, mem_region.size_bytes * bits_in_byte,
+                truncate_length * bits_in_byte))
 
             # Save some information so we know where to insert the `~~ break ~~` line.
             data_line = (current_offset - current_truncation_delta) // 4
@@ -203,16 +240,21 @@
             current_truncation_delta += mem_region.size_bytes - truncate_length
 
         else:
-            verbose_regions.append("{}:{}".format(name, mem_region.size_bytes * bits_in_byte))
+            verbose_regions.append("{}:{}".format(
+                name, mem_region.size_bytes * bits_in_byte))
 
         current_offset = new_offset + mem_region.size_bytes
 
     # Add a field for the image itself:
-    verbose_regions.append("code image:{}".format(truncate_length * bits_in_byte))
+    verbose_regions.append("code image:{}".format(truncate_length *
+                                                  bits_in_byte))
     truncation_lines.append((current_offset - current_truncation_delta) // 4)
 
     protocol_input = ",".join(verbose_regions)
-    protocol_result = subprocess.run(["protocol", "--bits", "32", protocol_input], universal_newlines=True, capture_output=True)
+    protocol_result = subprocess.run(
+        ["protocol", "--bits", "32", protocol_input],
+        universal_newlines=True,
+        capture_output=True)
     protocol_output = protocol_result.stdout
 
     truncation_mark = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  break  ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
@@ -231,7 +273,6 @@
     print('Format description successfully written to {}.'.format(output_path))
 
 
-
 def main():
     ALL_PARTS = ["c", "rust", "format"]
 
@@ -240,6 +281,12 @@
         usage=USAGE,
         description=DESC)
 
+    parser.add_argument('-v',
+                        '--verbose',
+                        action='store_true',
+                        default=False,
+                        help='Print Extra Information.')
+
     parser.add_argument('--input-dir',
                         required=True,
                         type=Path,
@@ -268,7 +315,7 @@
     if 'all' in args.output_files:
         args.output_files += ALL_PARTS
 
-    regions, offsets = generate_defines(obj['fields'])
+    regions, offsets = generate_defines(obj['fields'], args.verbose)
 
     if "c" in args.output_files:
         generate_cheader(regions, offsets, args.input_dir, args.output_dir)