> On 19 May 2026, at 6:57 PM, Eric Auger <[email protected]> wrote:
> 
> !-------------------------------------------------------------------|
>  CAUTION: External Email
> 
> |-------------------------------------------------------------------!
> 
> Introduce a script that takes as input the Registers.json file
> delivered in the AARCHMRS Features Model downloadable from the
> Arm Developer A-Profile Architecture Exploration Tools page:
> https://urldefense.proofpoint.com/v2/url?u=https-3A__developer.arm.com_Architectures_A-2DProfile-2520Architecture-23Downloads&d=DwIDAg&c=s883GpUCOChKOHiocYtGcg&r=PGWMyignA0NiDmTlyP7vOTHozBws_VN86yrVmSMkBp0&m=lifWLuAPiWiPtZHA1AnNwE36XKErpJppnuqSRce8eEXbd2seIXgOyghkIcv1h_tk&s=HRujmTCQGTPR11d90Orv_1iwKOLX_k3RFbkK_mx7A1s&e=
>  
> and automates the generation of system register properties definitions.
> 
> generates target/arm/cpu-idregs.h.inc containing definitions for
> feature ID registers.
> 
> We only care about IDregs with opcodes satisfying:
> op0 = 3, op1 = {0,1,3}, crn = 0, crm within [0, 7], op2 within [0, 7]
> 
> Signed-off-by: Eric Auger <[email protected]>
> ---
> .../update-aarch64-cpu-sysreg-properties.py   | 168 ++++++++++++++++++
> 1 file changed, 168 insertions(+)
> create mode 100644 scripts/update-aarch64-cpu-sysreg-properties.py
> 
> diff --git a/scripts/update-aarch64-cpu-sysreg-properties.py 
> b/scripts/update-aarch64-cpu-sysreg-properties.py
> new file mode 100644
> index 0000000000..3571e228ee
> --- /dev/null
> +++ b/scripts/update-aarch64-cpu-sysreg-properties.py
> @@ -0,0 +1,168 @@
> +#!/usr/bin/env python3
> +
> +# This script takes as input the Registers.json file delivered in
> +# the AARCHMRS Features Model downloadable from the Arm Developer
> +# A-Profile Architecture Exploration Tools page:
> +# 
> https://urldefense.proofpoint.com/v2/url?u=https-3A__developer.arm.com_Architectures_A-2DProfile-2520Architecture-23Downloads&d=DwIDAg&c=s883GpUCOChKOHiocYtGcg&r=PGWMyignA0NiDmTlyP7vOTHozBws_VN86yrVmSMkBp0&m=lifWLuAPiWiPtZHA1AnNwE36XKErpJppnuqSRce8eEXbd2seIXgOyghkIcv1h_tk&s=HRujmTCQGTPR11d90Orv_1iwKOLX_k3RFbkK_mx7A1s&e=
>  
> +# and outputs target/arm/cpu-sysreg-properties.c content.
> +# There, initialize_cpu_sysreg_properties() populates arm64_id_regs array
> +# with the name of each ID register and definition of all its fields
> +# including their name and min/max bit under the form of the below pattern:
> +#
> +# /* CCSIDR2_EL1 */
> +# ARM64SysReg *CCSIDR2_EL1 = arm64_sysreg_get(CCSIDR2_EL1_IDX);
> +# CCSIDR2_EL1->name = "CCSIDR2_EL1";
> +# arm64_sysreg_add_field(CCSIDR2_EL1, "NumSets", 0, 23);
> +#
> +# Copyright (C) 2026 Red Hat, Inc.
> +#
> +# Authors: Eric Auger <[email protected]>
> +#
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +
> +import json
> +import os
> +import sys
> +from aarch64_sysreg_helpers import extract_idregs_from_registers_json
> +
> +def collect_fields(item, bit_offset=0):
> +    """
> +    Recursively finds all field-like objects, handling Fields.Array,
> +    Fields.ArrayField, and ConditionalField structures.
> +    Applies bit_offset from containers to child fields.
> +    """
> +    fields = []
> +    if not isinstance(item, dict):
> +        return fields
> +
> +    _type = item.get('_type', '')
> +
> +    # Array types (for example CLIDR_EL1 Ctype<n>, Ttype<n>)
> +    if _type == 'Fields.Array':
> +        name_template = item.get('name') or item.get('label', '')
> +        index_info = item.get('indexes', [{}])[0]
> +        start_idx = index_info.get('start', 0)
> +        count = index_info.get('width', 0)
> +
> +        full_range = item.get('rangeset', [{}])[0]
> +        bit_start = full_range.get('start', 0) + bit_offset
> +        elem_width = full_range.get('width', 0) // count if count else 0
> +
> +        for i in range(count):
> +            idx = start_idx + i
> +            # Correctly handle indexed names like Ctype1, Ctype2
> +            field_name = name_template.replace('<n>', str(idx))
> +            fields.append({
> +                'name': field_name,
> +                'rangeset': [{
> +                    'start': bit_start + (i * elem_width),
> +                    'width': elem_width
> +                }],
> +                '_type': 'Fields.Field'
> +            })
> +        return fields
> +
> +    # ConditionalFields
> +    elif _type == 'Fields.ConditionalField':
> +        inner_offset = bit_offset
> +        if item.get('rangeset'):
> +            # Parent container defines the absolute start bit
> +            inner_offset = item['rangeset'][0].get('start', bit_offset)
> +
> +        for entry in item.get('fields', []):
> +            inner = entry.get('field')
> +            if inner:
> +                fields.extend(collect_fields(inner, inner_offset))
> +        return fields
> +
> +    # Normal Field Types
> +    leaf_types = ['Fields.Field', 'Fields.ConstantField',
> +                  'Fields.EnumeratedField', 'Fields.Bitfield']
> +    if _type in leaf_types:
> +        field_copy = item.copy()
> +        if field_copy.get('rangeset'):
> +            new_ranges = []
> +            for r in field_copy['rangeset']:
> +                nr = r.copy()
> +                # Apply the cumulative offset to the field's start bit
> +                nr['start'] = r.get('start', 0) + bit_offset
> +                new_ranges.append(nr)
> +            field_copy['rangeset'] = new_ranges
> +        fields.append(field_copy)
> +        return fields
> +
> +    # Traverse the hierarchy for other cases
> +    for key in ['fields', 'values', 'fieldsets']:
> +        for nested in item.get(key, []):
> +            fields.extend(collect_fields(nested, bit_offset))
> +
> +    return fields
> +
> +
> +def generate_sysreg_properties_from_registers_json(id_reg_names, 
> raw_json_path):
> +    with open(raw_json_path, 'r') as f:
> +        register_data = json.load(f)
> +
> +    regs = {r.get('name'): r for r in register_data if r.get('_type') == 
> 'Register'}
> +
> +    final_output = ""
> +
> +    for reg_name in id_reg_names:
> +        register = regs.get(reg_name)
> +        if not register:
> +            continue
> +
> +        final_output += f"    IDREG_START({reg_name})\n"
> +
> +        unique_fields = {}
> +        for fieldset in register.get('fieldsets', []):
> +            candidates = collect_fields(fieldset)
> +            for val in candidates:
> +                name = (val.get('name') or val.get('label', '')).strip()
> +                if not name or "RESERVED" in name.upper():
> +                    continue
> +                for r in val.get('rangeset', []):
> +                    lsb = int(r.get('start'))
> +                    width = r.get('width')
> +                    msb = lsb + int(width) - 1
> +
> +                    # Only keep the fields with the highest MSB
> +                    # needed fir CCSIDR_EL1
> +                    if name not in unique_fields or msb > 
> unique_fields[name]['msb']:
> +                        unique_fields[name] = {'lsb': lsb, 'msb': msb, 
> 'width': width}
> +
> +        # Sort decreasing lsbs
> +        sorted_fields = sorted(unique_fields.items(),
> +                               key=lambda x: x[1]['lsb'], reverse=True)
> +
> +        for name, bits in sorted_fields:
> +            line = (f"    IDREG_FIELD({reg_name}, "
> +                    f"{name}, {bits['lsb']}, {bits['width']})\n")
> +            final_output += line
> +        final_output += f"    IDREG_END({reg_name})\n"
> +        final_output += "\n"
> +
> +    os.makedirs("target/arm", exist_ok=True)
> +    with open("target/arm/cpu-idregs.h.inc", 'w') as f:
> +        f.write("/* AUTOMATICALLY GENERATED, DO NOT MODIFY */\n\n")
> +        f.write("/* SPDX-License-Identifier: GPL-2.0-or-later */\n\n")
> +        f.write("/* IDREG_START(REG) */\n")
> +        f.write("/* IDREG_FIELD(REG, FIELD, SHIFT, LENGTH) */\n")
> +        f.write("/* ... */\n")
> +        f.write("/* IDREG_END(REG) */\n\n")
> +        f.write(final_output)
> +
> +if __name__ == "__main__":
> +    if len(sys.argv) < 2:
> +        print("Usage: python scripts/update-aarch64-cpu-sysreg-properties.py 
> "
> +              "<path_to_registers_json>")
> +    else:
> +        json_path = sys.argv[1]
> +
> +        id_regs_dict = extract_idregs_from_registers_json(json_path)
> +        sorted_names = sorted(id_regs_dict.keys())
> +
> +        if sorted_names:
> +            generate_sysreg_properties_from_registers_json(sorted_names, 
> json_path)
> +            print("Generated target/arm/cpu-idregs.h.inc")
> -- 
> 2.53.0
> 

Hi Eric,

Can we also have the arch-defined values in here? It is already available in 
Registers.json easily, would be great if we can validate at-least the user
set values are architecturally valid.

Warm Regards,
Khushit

Reply via email to