[util] Add Offset Support to ROM_EXT Manifest Generator
This adds the ability to generate "offsets", which are like fields but
they don't have any size definitions. We need these for the entry point
and the interrupt vector, but I also added one so we know where the
signed area starts.
I also added support for specifying a non-default alignment, which
ensures fields and offsets are at their required alignment.
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 3256770..654c5dd 100755
--- a/util/rom-ext-manifest-generator.py
+++ b/util/rom-ext-manifest-generator.py
@@ -2,6 +2,38 @@
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
+"""
+This script generates various descriptions of the ROM_EXT manifest format based
+on the `sw/device/rom_exts/manifest.hjson` machine-readable description of that
+format.
+
+The main part of `mainfest.hjson` is a list of fields described using a list of
+dicts. These features are given in sequential order.
+
+Each dict can describe one of the following kinds of data format features:
+- `type: reserved` which is a reserved area of a certain size. These definitions
+ are not named, and definitions are not produced for them.
+- `type: field` which is a readable field of a certain size. We generate
+ definitions for the start offset of the field, and for the field size (in both
+ bytes and words). These must have a name.
+- `type: offset` which is a named offset into the ROM_EXT image. This implicitly
+ has a size of zero, but no size definitions are given for these fields.
+ Offsets with an alignment can move any features that follow them. These must
+ have a name.
+
+All data format features have a default alignment of 32-bits. This alignment can
+be reduced or expanded with the `alignment` key.
+
+All sizes and alignments are given in *bits*, not bytes.
+
+All fields and offsets must have a name, and optionally a description in the
+`desc` key.
+
+The generator currently produces the following files (into the given output
+directory):
+- `template.h`, from `sw/device/rom_exts/manifest.h.tpl` which provides field
+ size and offset preprocessor definitions for use from C.
+"""
import argparse
from pathlib import Path
@@ -21,6 +53,14 @@
Directory where manifest.h will be created.
"""
+class MemoryOffset(object):
+ def __init__(self, name, offset):
+ self.name = name
+ self.offset = offset
+
+ def offset_name(self):
+ return self.name + Name(["offset"])
+
def generate_cheader(fields, input_dir, output_dir):
""" Generates C header file from the `template_file`.
@@ -35,21 +75,38 @@
base_name = Name.from_snake_case("ROM_EXT")
- items = []
- offset = 0
+ regions = []
+ offsets = []
+ current_offset_bytes = 0
for field in fields:
- 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, offset, size_bytes)
- items.append((field['name'], region))
- offset += size_bytes
+ required_alignment_bits = field.get("alignment", 32)
+ assert required_alignment_bits % 8 == 0
+ 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`
+ # that is greater than or equal to `current_offset_bytes`.
+ current_offset_bytes = (current_offset_bytes + required_alignment_bytes - 1) \
+ & ~(required_alignment_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))
+
+ 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)
+ regions.append((field['name'], region))
+ current_offset_bytes += size_bytes
with template_path.open('r') as f:
template = Template(f.read())
- header = template.render(items=items)
+ header = template.render(regions=regions, offsets=offsets)
with output_path.open('w') as f:
f.write(header)