1#-------------------------------------------------------------------------------
2# Copyright (c) 2023, Arm Limited. All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6#-------------------------------------------------------------------------------
7import yaml
8import logging, logging.handlers
9import os
10import argparse
11
12from yaml.loader import SafeLoader
13
14# logging setup
15logging.basicConfig(level=logging.WARNING, format='%(name)s - %(levelname)s - %(message)s')
16
17parser = argparse.ArgumentParser()
18parser.add_argument("--input_file", help="DMA programs high level yaml file", required=True)
19parser.add_argument("--output_dir", help="Output directory", required=True)
20args = parser.parse_args()
21
22class Loader(yaml.SafeLoader):
23    def __init__(self, stream):
24        self._root = os.path.split(stream.name)[0]
25        super(Loader, self).__init__(stream)
26
27    # load nested yaml files (if any)
28    def include(self, node):
29        filepath = os.path.join(self._root, self.construct_scalar(node))
30        command_name = os.path.basename(self.construct_scalar(node)).replace('.yaml', '')
31
32        with open(filepath, 'r') as f:
33            child_node = yaml.load(f, Loader)
34            child_node['command_name'] = command_name
35            return child_node
36
37Loader.add_constructor('!include', Loader.include)
38
39# calculate header word from bit fields
40def calculate_header_word(header):
41    final_val=0
42    for i in range(len(header)):
43        for key in header[i]:
44            val=header[i][key]
45            final_val = final_val + (val << i)
46    return final_val
47
48
49def parse_store(command, key, reference_string):
50    return command_storage_locations[reference_string]
51
52def parse_exec(command, key, reference_string):
53    command_location = command_execution_locations[reference_string]
54    # Need to set bit 0 if the link should be executed
55    if key != 'SRCADDR' and key != 'DESADDR':
56        try:
57            if command['execute_link']:
58                command_location += 1
59        except KeyError:
60            # Default to performing the link
61            command_location += 1
62    return command_location
63
64def parse_size(command, key, reference_string):
65    size = sizes[reference_string]
66    if key == 'XSIZE':
67        size = size // 4
68        size &= 0xFFFF
69        size |= size << 16
70    elif key != 'SRCADDR' and key != 'DESADDR':
71        # Convert size to words
72        size = size // 4
73    return size
74
75def parse_base_address(command, key, reference_string):
76    return location_base_addresses[reference_string]
77
78def parse(command, key, reference_string):
79    if "-" in reference_string:
80        split = reference_string.split(" - ", maxsplit=1)
81        return parse(command, key, split[0].lstrip()) - parse(command, key, split[1])
82
83    if "+" in reference_string:
84        split = reference_string.split(" + ", maxsplit=1)
85        return parse(command, key, split[0].lstrip()) + parse(command, key, split[1])
86
87    if "/" in reference_string:
88        split = reference_string.split(" / ", maxsplit=1)
89        return parse(command, key, split[0].lstrip()) // parse(command, key, split[1])
90
91    if "*" in reference_string:
92        split = reference_string.split(" * ", maxsplit=1)
93        return parse(command, key, split[0].lstrip()) * parse(command, key, split[1])
94
95    if "_store_addr" in reference_string:
96        value = parse_store(command, key, reference_string.replace("_store_addr", ""))
97    elif "_exec_addr" in reference_string:
98        value = parse_exec(command, key, reference_string.replace("_exec_addr", ""))
99    elif "_size" in reference_string:
100        value = parse_size(command, key, reference_string.replace("_size", ""))
101    elif "_base_address" in reference_string:
102        value = parse_base_address(command, key, reference_string.replace("_base_address", ""))
103    else:
104        value = int(reference_string, 0)
105
106    logging.debug('resolving reference {} as {}'.format(reference_string, hex(value)))
107    return value
108
109# load top level input config file
110with open(args.input_file, 'r') as f:
111   data = yaml.load(f, Loader)
112
113program_count = len(data["program"])
114
115location_base_addresses={}
116location_next_addresses={}
117location_word_arrays={}
118output_locations={}
119
120command_storage_locations = {}
121command_execution_locations = {}
122sizes = {}
123
124reserved_keys=['command_name', 'execute_link']
125
126# Setup locations
127for location in data['locations']:
128    location_word_arrays[location] = []
129    location_base_addresses[location] = data['locations'][location]['base_address']
130    try:
131        reserved_size = data['locations'][location]['reserved']
132    except KeyError:
133        reserved_size = 0
134    location_next_addresses[location] = data['locations'][location]['base_address'] + reserved_size
135    try:
136        sizes[location] = data['locations'][location]['size']
137        size = sizes[location]
138    except KeyError:
139        size = 'unknown'
140        pass
141    logging.info('Location {} at {}, size {}, first usable address {}'.format(
142        location,
143        hex(location_base_addresses[location]),
144        size,
145        hex(location_next_addresses[location])))
146
147for program in data['program']:
148    commands = program['commands']
149    sizes[program['name']] = 0
150    for command in commands:
151        command_size = sum([1 for key in command if key not in reserved_keys]) * 4
152        sizes[command['command_name']] = command_size
153        sizes[program['name']] += command_size
154        command_storage_locations[command['command_name']] = location_next_addresses[program['storage_location']]
155        command_execution_locations[command['command_name']] = location_next_addresses[program['execution_location']]
156        location_next_addresses[program['storage_location']] += command_size
157        # don't increment twice if the storage location is the same as the
158        # execution location
159        if location_next_addresses[program['storage_location']] != location_next_addresses[program['execution_location']]:
160            location_next_addresses[program['execution_location']] += command_size
161        output_locations[program['storage_location']] = True
162        logging.info('Command {} stored at {}, executed from {}, size {}'.format(
163            command['command_name'],
164            hex(command_storage_locations[command['command_name']]),
165            hex(command_execution_locations[command['command_name']]),
166            sizes[command['command_name']],
167            ))
168    logging.info('Program {} stored at {}, executed from {}, size {}'.format(
169        program['name'],
170        hex(location_next_addresses[program['storage_location']] - sizes[program['name']]),
171        hex(location_next_addresses[program['execution_location']] - sizes[program['name']]),
172        sizes[program['name']],
173        ))
174
175for program in data['program']:
176    commands = program['commands']
177    for command in commands:
178        command["Header"] = calculate_header_word(command["Header"])
179        command_array = []
180        for key in command:
181            if key in reserved_keys:
182                continue
183            try:
184                command_array.append(int(command[key]))
185            except ValueError:
186                command_array.append(int(parse(command, key, command[key])))
187        location_word_arrays[program['storage_location']] += command_array
188
189for location in output_locations:
190    try:
191        reserved_size = data['locations'][location]['reserved']
192    except KeyError:
193        reserved_size = 0
194    if len(location_word_arrays[location]) * 4 + reserved_size > sizes[location]:
195        raise Exception
196    with open(os.path.join(args.output_dir, location + "_dma_ics.bin"), mode="wb") as bin_file:
197        with open(os.path.join(args.output_dir, location + "_dma_ics.hex"), mode="wt") as hex_file:
198            logging.info('Writing output binary for location {} to file {} of size {}'.format(
199                location, os.path.join(args.output_dir, location + "_dma_ics.bin"),
200                len(location_word_arrays[location]) * 4))
201            logging.info('Writing output hex for location {} to file {} of size {}'.format(
202                location, os.path.join(args.output_dir, location + "_dma_ics.hex"),
203                len(location_word_arrays[location]) * 4))
204            for word in location_word_arrays[location]:
205                bin_file.write(word.to_bytes(4, byteorder='little'))
206                hex_file.write(word.to_bytes(4, byteorder='big').hex() + '\n')
207