File: parse_versions_for_dts.py

package info (click to toggle)
uhd 4.8.0.0%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 183,172 kB
  • sloc: cpp: 279,415; python: 109,850; ansic: 103,348; vhdl: 57,230; tcl: 20,007; xml: 8,581; makefile: 2,863; sh: 2,797; pascal: 230; javascript: 120; csh: 94; asm: 20; perl: 11
file content (153 lines) | stat: -rwxr-xr-x 5,889 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env python3
#
# Copyright 2021 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#

"""
This script parses versioning information from a Verilog file and generates a
devicetree include file (.dtsi) that follows the syntax that is required by MPM
for version checking.

usage: parse-versions-for-dts.py [-h] --input INPUT --output OUTPUT
                                 --components COMPONENTS

Arguments:

  -h, --help            show this help message and exit
  --input INPUT         The input file(s) to parse. A comma separated list can
                        be used to specify multiple files
  --output OUTPUT       The output file
  --components COMPONENTS
                        The components for which the version information is
                        extracted. A comma separated list can be used to
                        specify multiple components.

Example call:

./parse-versions-for-dts.py \
        --input regmap/global_regs_regmap_utils.vh \
        --output build/component_versions.dtsi \
        --components fpga

Example for variable definitions in a Verilog file
(regmap/global_regs_regmap_utils.vh):

    localparam FPGA_CURRENT_VERSION_MAJOR = 'h4;
    localparam FPGA_CURRENT_VERSION_MINOR = 'h2;
    localparam FPGA_CURRENT_VERSION_BUILD = 'h18;
    localparam FPGA_OLDEST_COMPATIBLE_VERSION_MAJOR = 'h4;
    localparam FPGA_OLDEST_COMPATIBLE_VERSION_MINOR = 'h0;
    localparam FPGA_OLDEST_COMPATIBLE_VERSION_BUILD = 'h0;
    localparam FPGA_VERSION_LAST_MODIFIED_TIME = 'h20111013;

Example for the generated dtsi include file (build/component_versions.dtsi)

    // mpm_version fpga_current_version 4.2.24
    // mpm_version fpga_oldest_compatible_version 4.0.0
    // mpm_version fpga_version_last_modified_time 0x20111013
"""
import argparse
import re
import sys

def parse_args():
    """Parser for the command line arguments"""
    parser = argparse.ArgumentParser(description='Parse variables from Verilog files')
    parser.add_argument('--input', required=True,
                        help='The input file(s) to parse. A comma separated ' \
                             'list can be used to specify multiple files')
    parser.add_argument('--output', required=True, help='The output file')
    parser.add_argument('--components', required=True,
                        help='The components for which the version information ' \
                             'is extracted. A comma separated list can be used ' \
                             'to specify multiple components.')
    args = parser.parse_args()
    args.input = args.input.split(',')
    args.components = args.components.split(',')
    return args

def parse_variables_from_verilog(filename):
    """Parses variables from a Verilog file and stores them in a dict.
    The following syntax is supported:
      localparam <VARIABLE> = <VALUE>;
    :param str filename: input filename
    """
    pattern_dec = re.compile(r'^(\d*)$')
    pattern_hex = re.compile(r'^\d*\'h(\w*)$')

    def _convert_verilog_datatype(str_value):
        """Converts the Verilog value string to the corresponding Python datatype
        The following notations are supported:
        - decimal (e.g. 1)
        - hexadecimal (e.g. 'h4)
        - hexadecimal with specification of the variable width (e.g. 32'h4)
        :param str str_value: the string value to be converted
        """
        match_dec = pattern_dec.match(str_value)
        if match_dec:
            return int(match_dec.group(1))
        match_hex = pattern_hex.match(str_value)
        if match_hex:
            return int(match_hex.group(1), 16)
        return '?'

    with open(filename) as f:
        lines = f.read().splitlines()
    pattern = re.compile(r'\s*localparam (\S*)\s*=\s*(\S*);')
    variables = {}
    for line in lines:
        match = pattern.match(line)
        if match:
            key = match.group(1)
            value = _convert_verilog_datatype(match.group(2))
            variables[key] = value
    return variables

def map_variables(variables, component):
    """map the variables as they shall appear in the generated dtsi file
    :param dict variables: the variables that were parsed from the Verilog file(s)
    :param str component: the name of the component
    """
    COMPONENT = component.upper()
    mapped_vars = {
        '{}_current_version'.format(component): '{}.{}.{}'.format(
            variables['{}_CURRENT_VERSION_MAJOR'.format(COMPONENT)],
            variables['{}_CURRENT_VERSION_MINOR'.format(COMPONENT)],
            variables['{}_CURRENT_VERSION_BUILD'.format(COMPONENT)]
        ),
        '{}_oldest_compatible_version'.format(component): '{}.{}.{}'.format(
            variables['{}_CURRENT_VERSION_MAJOR'.format(COMPONENT)],
            variables['{}_CURRENT_VERSION_MINOR'.format(COMPONENT)],
            variables['{}_CURRENT_VERSION_BUILD'.format(COMPONENT)]
        ),
        '{}_version_last_modified_time'.format(component): '0x{:X}'.format(
            variables['{}_VERSION_LAST_MODIFIED_TIME'.format(COMPONENT)]
        ),
    }
    return mapped_vars

def generate_dtsi_file(filename, mapped_vars):
    """Generate the dtsi file
    :param str filename: the output filename
    :param dict mapped_vars: the variables that shall be written to the file
    """
    with open(filename, 'w') as f:
        for k, v in mapped_vars.items():
            f.write('// mpm_version {} {}\n'.format(k, v))

def main():
    """ main function """
    args = parse_args()
    variables = {}
    mapped_vars = {}
    for file in args.input:
        variables.update(parse_variables_from_verilog(file))
    for component in args.components:
        mapped_vars.update(map_variables(variables, component))
    generate_dtsi_file(args.output, mapped_vars)
    return True

if __name__ == '__main__':
    sys.exit(not main())