1b8e80941Smrgfrom __future__ import print_function, division, unicode_literals
2b8e80941Smrg
3b8e80941SmrgCopyRight = '''
4b8e80941Smrg/*
5b8e80941Smrg * Copyright 2015 Advanced Micro Devices, Inc.
6b8e80941Smrg *
7b8e80941Smrg * Permission is hereby granted, free of charge, to any person obtaining a
8b8e80941Smrg * copy of this software and associated documentation files (the "Software"),
9b8e80941Smrg * to deal in the Software without restriction, including without limitation
10b8e80941Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub
11b8e80941Smrg * license, and/or sell copies of the Software, and to permit persons to whom
12b8e80941Smrg * the Software is furnished to do so, subject to the following conditions:
13b8e80941Smrg *
14b8e80941Smrg * The above copyright notice and this permission notice (including the next
15b8e80941Smrg * paragraph) shall be included in all copies or substantial portions of the
16b8e80941Smrg * Software.
17b8e80941Smrg *
18b8e80941Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19b8e80941Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20b8e80941Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21b8e80941Smrg * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22b8e80941Smrg * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23b8e80941Smrg * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24b8e80941Smrg * USE OR OTHER DEALINGS IN THE SOFTWARE.
25b8e80941Smrg *
26b8e80941Smrg */
27b8e80941Smrg'''
28b8e80941Smrg
29b8e80941Smrgimport collections
30b8e80941Smrgimport functools
31b8e80941Smrgimport itertools
32b8e80941Smrgimport os.path
33b8e80941Smrgimport re
34b8e80941Smrgimport sys
35b8e80941Smrg
36b8e80941Smrg
37b8e80941Smrgclass StringTable:
38b8e80941Smrg    """
39b8e80941Smrg    A class for collecting multiple strings in a single larger string that is
40b8e80941Smrg    used by indexing (to avoid relocations in the resulting binary)
41b8e80941Smrg    """
42b8e80941Smrg    def __init__(self):
43b8e80941Smrg        self.table = []
44b8e80941Smrg        self.length = 0
45b8e80941Smrg
46b8e80941Smrg    def add(self, string):
47b8e80941Smrg        # We might get lucky with string being a suffix of a previously added string
48b8e80941Smrg        for te in self.table:
49b8e80941Smrg            if te[0].endswith(string):
50b8e80941Smrg                idx = te[1] + len(te[0]) - len(string)
51b8e80941Smrg                te[2].add(idx)
52b8e80941Smrg                return idx
53b8e80941Smrg
54b8e80941Smrg        idx = self.length
55b8e80941Smrg        self.table.append((string, idx, set((idx,))))
56b8e80941Smrg        self.length += len(string) + 1
57b8e80941Smrg
58b8e80941Smrg        return idx
59b8e80941Smrg
60b8e80941Smrg    def emit(self, filp, name, static=True):
61b8e80941Smrg        """
62b8e80941Smrg        Write
63b8e80941Smrg        [static] const char name[] = "...";
64b8e80941Smrg        to filp.
65b8e80941Smrg        """
66b8e80941Smrg        fragments = [
67b8e80941Smrg            '"%s\\0" /* %s */' % (
68b8e80941Smrg                te[0].encode('unicode_escape').decode(),
69b8e80941Smrg                ', '.join(str(idx) for idx in sorted(te[2]))
70b8e80941Smrg            )
71b8e80941Smrg            for te in self.table
72b8e80941Smrg        ]
73b8e80941Smrg        filp.write('%sconst char %s[] =\n%s;\n' % (
74b8e80941Smrg            'static ' if static else '',
75b8e80941Smrg            name,
76b8e80941Smrg            '\n'.join('\t' + fragment for fragment in fragments)
77b8e80941Smrg        ))
78b8e80941Smrg
79b8e80941Smrgclass IntTable:
80b8e80941Smrg    """
81b8e80941Smrg    A class for collecting multiple arrays of integers in a single big array
82b8e80941Smrg    that is used by indexing (to avoid relocations in the resulting binary)
83b8e80941Smrg    """
84b8e80941Smrg    def __init__(self, typename):
85b8e80941Smrg        self.typename = typename
86b8e80941Smrg        self.table = []
87b8e80941Smrg        self.idxs = set()
88b8e80941Smrg
89b8e80941Smrg    def add(self, array):
90b8e80941Smrg        # We might get lucky and find the array somewhere in the existing data
91b8e80941Smrg        try:
92b8e80941Smrg            idx = 0
93b8e80941Smrg            while True:
94b8e80941Smrg                idx = self.table.index(array[0], idx, len(self.table) - len(array) + 1)
95b8e80941Smrg
96b8e80941Smrg                for i in range(1, len(array)):
97b8e80941Smrg                    if array[i] != self.table[idx + i]:
98b8e80941Smrg                        break
99b8e80941Smrg                else:
100b8e80941Smrg                    self.idxs.add(idx)
101b8e80941Smrg                    return idx
102b8e80941Smrg
103b8e80941Smrg                idx += 1
104b8e80941Smrg        except ValueError:
105b8e80941Smrg            pass
106b8e80941Smrg
107b8e80941Smrg        idx = len(self.table)
108b8e80941Smrg        self.table += array
109b8e80941Smrg        self.idxs.add(idx)
110b8e80941Smrg        return idx
111b8e80941Smrg
112b8e80941Smrg    def emit(self, filp, name, static=True):
113b8e80941Smrg        """
114b8e80941Smrg        Write
115b8e80941Smrg        [static] const typename name[] = { ... };
116b8e80941Smrg        to filp.
117b8e80941Smrg        """
118b8e80941Smrg        idxs = sorted(self.idxs) + [len(self.table)]
119b8e80941Smrg
120b8e80941Smrg        fragments = [
121b8e80941Smrg            ('\t/* %s */ %s' % (
122b8e80941Smrg                idxs[i],
123b8e80941Smrg                ' '.join((str(elt) + ',') for elt in self.table[idxs[i]:idxs[i+1]])
124b8e80941Smrg            ))
125b8e80941Smrg            for i in range(len(idxs) - 1)
126b8e80941Smrg        ]
127b8e80941Smrg
128b8e80941Smrg        filp.write('%sconst %s %s[] = {\n%s\n};\n' % (
129b8e80941Smrg            'static ' if static else '',
130b8e80941Smrg            self.typename, name,
131b8e80941Smrg            '\n'.join(fragments)
132b8e80941Smrg        ))
133b8e80941Smrg
134b8e80941Smrgclass Field:
135b8e80941Smrg    def __init__(self, reg, s_name):
136b8e80941Smrg        self.s_name = s_name
137b8e80941Smrg        self.name = strip_prefix(s_name)
138b8e80941Smrg        self.values = []
139b8e80941Smrg
140b8e80941Smrg    def format(self, string_table, idx_table):
141b8e80941Smrg        if len(self.values):
142b8e80941Smrg            values_offsets = []
143b8e80941Smrg            for value in self.values:
144b8e80941Smrg                while value[1] >= len(values_offsets):
145b8e80941Smrg                    values_offsets.append(-1)
146b8e80941Smrg                values_offsets[value[1]] = string_table.add(strip_prefix(value[0]))
147b8e80941Smrg            return '{%s, %s(~0u), %s, %s}' % (
148b8e80941Smrg                string_table.add(self.name), self.s_name,
149b8e80941Smrg                len(values_offsets), idx_table.add(values_offsets))
150b8e80941Smrg        else:
151b8e80941Smrg            return '{%s, %s(~0u)}' % (string_table.add(self.name), self.s_name)
152b8e80941Smrg
153b8e80941Smrg    def __eq__(self, other):
154b8e80941Smrg        return (self.s_name == other.s_name and
155b8e80941Smrg                self.name == other.name and
156b8e80941Smrg                len(self.values) == len(other.values) and
157b8e80941Smrg                all(a[0] == b[0] and a[1] == b[1] for a, b, in zip(self.values, other.values)))
158b8e80941Smrg
159b8e80941Smrg    def __ne__(self, other):
160b8e80941Smrg        return not (self == other)
161b8e80941Smrg
162b8e80941Smrg
163b8e80941Smrgclass FieldTable:
164b8e80941Smrg    """
165b8e80941Smrg    A class for collecting multiple arrays of register fields in a single big
166b8e80941Smrg    array that is used by indexing (to avoid relocations in the resulting binary)
167b8e80941Smrg    """
168b8e80941Smrg    def __init__(self):
169b8e80941Smrg        self.table = []
170b8e80941Smrg        self.idxs = set()
171b8e80941Smrg        self.name_to_idx = collections.defaultdict(lambda: [])
172b8e80941Smrg
173b8e80941Smrg    def add(self, array):
174b8e80941Smrg        """
175b8e80941Smrg        Add an array of Field objects, and return the index of where to find
176b8e80941Smrg        the array in the table.
177b8e80941Smrg        """
178b8e80941Smrg        # Check if we can find the array in the table already
179b8e80941Smrg        for base_idx in self.name_to_idx.get(array[0].name, []):
180b8e80941Smrg            if base_idx + len(array) > len(self.table):
181b8e80941Smrg                continue
182b8e80941Smrg
183b8e80941Smrg            for i, a in enumerate(array):
184b8e80941Smrg                b = self.table[base_idx + i]
185b8e80941Smrg                if a != b:
186b8e80941Smrg                    break
187b8e80941Smrg            else:
188b8e80941Smrg                return base_idx
189b8e80941Smrg
190b8e80941Smrg        base_idx = len(self.table)
191b8e80941Smrg        self.idxs.add(base_idx)
192b8e80941Smrg
193b8e80941Smrg        for field in array:
194b8e80941Smrg            self.name_to_idx[field.name].append(len(self.table))
195b8e80941Smrg            self.table.append(field)
196b8e80941Smrg
197b8e80941Smrg        return base_idx
198b8e80941Smrg
199b8e80941Smrg    def emit(self, filp, string_table, idx_table):
200b8e80941Smrg        """
201b8e80941Smrg        Write
202b8e80941Smrg        static const struct si_field sid_fields_table[] = { ... };
203b8e80941Smrg        to filp.
204b8e80941Smrg        """
205b8e80941Smrg        idxs = sorted(self.idxs) + [len(self.table)]
206b8e80941Smrg
207b8e80941Smrg        filp.write('static const struct si_field sid_fields_table[] = {\n')
208b8e80941Smrg
209b8e80941Smrg        for start, end in zip(idxs, idxs[1:]):
210b8e80941Smrg            filp.write('\t/* %s */\n' % (start))
211b8e80941Smrg            for field in self.table[start:end]:
212b8e80941Smrg                filp.write('\t%s,\n' % (field.format(string_table, idx_table)))
213b8e80941Smrg
214b8e80941Smrg        filp.write('};\n')
215b8e80941Smrg
216b8e80941Smrg
217b8e80941Smrgclass Reg:
218b8e80941Smrg    def __init__(self, r_name):
219b8e80941Smrg        self.r_name = r_name
220b8e80941Smrg        self.name = strip_prefix(r_name)
221b8e80941Smrg        self.fields = []
222b8e80941Smrg
223b8e80941Smrg    def __eq__(self, other):
224b8e80941Smrg        if not isinstance(other, Reg):
225b8e80941Smrg            return False
226b8e80941Smrg        return (self.r_name == other.r_name and
227b8e80941Smrg                self.name == other.name and
228b8e80941Smrg                len(self.fields) == len(other.fields) and
229b8e80941Smrg                all(a == b for a, b in zip(self.fields, other.fields)))
230b8e80941Smrg
231b8e80941Smrg    def __ne__(self, other):
232b8e80941Smrg        return not (self == other)
233b8e80941Smrg
234b8e80941Smrg
235b8e80941Smrgdef strip_prefix(s):
236b8e80941Smrg    '''Strip prefix in the form ._.*_, e.g. R_001234_'''
237b8e80941Smrg    return s[s[2:].find('_')+3:]
238b8e80941Smrg
239b8e80941Smrg
240b8e80941Smrgclass Asic:
241b8e80941Smrg    """
242b8e80941Smrg    Store the registers of one ASIC class / group of classes.
243b8e80941Smrg    """
244b8e80941Smrg    def __init__(self, name):
245b8e80941Smrg        self.name = name
246b8e80941Smrg        self.registers = []
247b8e80941Smrg
248b8e80941Smrg    def parse(self, filp, packets, older_asics):
249b8e80941Smrg        """
250b8e80941Smrg        Parse registers from the given header file. Packets are separately
251b8e80941Smrg        stored in the packets array.
252b8e80941Smrg        """
253b8e80941Smrg        for line in filp:
254b8e80941Smrg            if not line.startswith('#define '):
255b8e80941Smrg                continue
256b8e80941Smrg
257b8e80941Smrg            line = line[8:].strip()
258b8e80941Smrg
259b8e80941Smrg            if line.startswith('R_'):
260b8e80941Smrg                name = line.split()[0]
261b8e80941Smrg
262b8e80941Smrg                for it in self.registers:
263b8e80941Smrg                    if it.r_name == name:
264b8e80941Smrg                        sys.exit('Duplicate register define: %s' % (name))
265b8e80941Smrg                else:
266b8e80941Smrg                    reg = Reg(name)
267b8e80941Smrg                    self.registers.append(reg)
268b8e80941Smrg
269b8e80941Smrg            elif line.startswith('S_'):
270b8e80941Smrg                name = line[:line.find('(')]
271b8e80941Smrg
272b8e80941Smrg                for it in reg.fields:
273b8e80941Smrg                    if it.s_name == name:
274b8e80941Smrg                        sys.exit('Duplicate field define: %s' % (name))
275b8e80941Smrg                else:
276b8e80941Smrg                    field = Field(reg, name)
277b8e80941Smrg                    reg.fields.append(field)
278b8e80941Smrg
279b8e80941Smrg            elif line.startswith('V_'):
280b8e80941Smrg                split = line.split()
281b8e80941Smrg                name = split[0]
282b8e80941Smrg                value = int(split[1], 0)
283b8e80941Smrg
284b8e80941Smrg                for (n,v) in field.values:
285b8e80941Smrg                    if n == name:
286b8e80941Smrg                        sys.exit('Duplicate value define: name = ' + name)
287b8e80941Smrg
288b8e80941Smrg                field.values.append((name, value))
289b8e80941Smrg
290b8e80941Smrg            elif line.startswith('PKT3_') and line.find('0x') != -1 and line.find('(') == -1:
291b8e80941Smrg                packets.append(line.split()[0])
292b8e80941Smrg
293b8e80941Smrg        # Copy values for corresponding fields from older ASICs if they were
294b8e80941Smrg        # not redefined
295b8e80941Smrg        for reg in self.registers:
296b8e80941Smrg            old_reg = False
297b8e80941Smrg            for field in reg.fields:
298b8e80941Smrg                if len(field.values) > 0:
299b8e80941Smrg                    continue
300b8e80941Smrg                if old_reg is False:
301b8e80941Smrg                    for old_reg in itertools.chain(
302b8e80941Smrg                            *(asic.registers for asic in reversed(older_asics))):
303b8e80941Smrg                        if old_reg.name == reg.name:
304b8e80941Smrg                            break
305b8e80941Smrg                    else:
306b8e80941Smrg                        old_reg = None
307b8e80941Smrg                if old_reg is not None:
308b8e80941Smrg                    for old_field in old_reg.fields:
309b8e80941Smrg                        if old_field.name == field.name:
310b8e80941Smrg                            field.values = old_field.values
311b8e80941Smrg                            break
312b8e80941Smrg
313b8e80941Smrg        # Copy fields to indexed registers which have their fields only defined
314b8e80941Smrg        # at register index 0.
315b8e80941Smrg        # For example, copy fields from CB_COLOR0_INFO to CB_COLORn_INFO, n > 0.
316b8e80941Smrg        match_number = re.compile('[0-9]+')
317b8e80941Smrg        reg_dict = dict()
318b8e80941Smrg
319b8e80941Smrg        # Create a dict of registers with fields and '0' in their name
320b8e80941Smrg        for reg in self.registers:
321b8e80941Smrg            if len(reg.fields) and reg.name.find('0') != -1:
322b8e80941Smrg                reg_dict[reg.name] = reg
323b8e80941Smrg
324b8e80941Smrg        # Assign fields
325b8e80941Smrg        for reg in self.registers:
326b8e80941Smrg            if not len(reg.fields):
327b8e80941Smrg                reg0 = reg_dict.get(match_number.sub('0', reg.name))
328b8e80941Smrg                if reg0 != None:
329b8e80941Smrg                    reg.fields = reg0.fields
330b8e80941Smrg
331b8e80941Smrg
332b8e80941Smrgdef write_tables(asics, packets):
333b8e80941Smrg    strings = StringTable()
334b8e80941Smrg    strings_offsets = IntTable("int")
335b8e80941Smrg    fields = FieldTable()
336b8e80941Smrg
337b8e80941Smrg    print('/* This file is autogenerated by sid_tables.py from sid.h. Do not edit directly. */')
338b8e80941Smrg    print()
339b8e80941Smrg    print(CopyRight.strip())
340b8e80941Smrg    print('''
341b8e80941Smrg#ifndef SID_TABLES_H
342b8e80941Smrg#define SID_TABLES_H
343b8e80941Smrg
344b8e80941Smrgstruct si_field {
345b8e80941Smrg        unsigned name_offset;
346b8e80941Smrg        unsigned mask;
347b8e80941Smrg        unsigned num_values;
348b8e80941Smrg        unsigned values_offset; /* offset into sid_strings_offsets */
349b8e80941Smrg};
350b8e80941Smrg
351b8e80941Smrgstruct si_reg {
352b8e80941Smrg        unsigned name_offset;
353b8e80941Smrg        unsigned offset;
354b8e80941Smrg        unsigned num_fields;
355b8e80941Smrg        unsigned fields_offset;
356b8e80941Smrg};
357b8e80941Smrg
358b8e80941Smrgstruct si_packet3 {
359b8e80941Smrg        unsigned name_offset;
360b8e80941Smrg        unsigned op;
361b8e80941Smrg};
362b8e80941Smrg''')
363b8e80941Smrg
364b8e80941Smrg    print('static const struct si_packet3 packet3_table[] = {')
365b8e80941Smrg    for pkt in packets:
366b8e80941Smrg        print('\t{%s, %s},' % (strings.add(pkt[5:]), pkt))
367b8e80941Smrg    print('};')
368b8e80941Smrg    print()
369b8e80941Smrg
370b8e80941Smrg    regs = {}
371b8e80941Smrg    for asic in asics:
372b8e80941Smrg        print('static const struct si_reg %s_reg_table[] = {' % (asic.name))
373b8e80941Smrg        for reg in asic.registers:
374b8e80941Smrg            # Only output a register that was changed or added relative to
375b8e80941Smrg            # the previous generation
376b8e80941Smrg            previous = regs.get(reg.r_name, None)
377b8e80941Smrg            if previous == reg:
378b8e80941Smrg                continue
379b8e80941Smrg
380b8e80941Smrg            if len(reg.fields):
381b8e80941Smrg                print('\t{%s, %s, %s, %s},' % (strings.add(reg.name), reg.r_name,
382b8e80941Smrg                    len(reg.fields), fields.add(reg.fields)))
383b8e80941Smrg            else:
384b8e80941Smrg                print('\t{%s, %s},' % (strings.add(reg.name), reg.r_name))
385b8e80941Smrg
386b8e80941Smrg            regs[reg.r_name] = reg
387b8e80941Smrg        print('};')
388b8e80941Smrg        print()
389b8e80941Smrg
390b8e80941Smrg    fields.emit(sys.stdout, strings, strings_offsets)
391b8e80941Smrg
392b8e80941Smrg    print()
393b8e80941Smrg
394b8e80941Smrg    strings.emit(sys.stdout, "sid_strings")
395b8e80941Smrg
396b8e80941Smrg    print()
397b8e80941Smrg
398b8e80941Smrg    strings_offsets.emit(sys.stdout, "sid_strings_offsets")
399b8e80941Smrg
400b8e80941Smrg    print()
401b8e80941Smrg    print('#endif')
402b8e80941Smrg
403b8e80941Smrg
404b8e80941Smrgdef main():
405b8e80941Smrg    asics = []
406b8e80941Smrg    packets = []
407b8e80941Smrg    for arg in sys.argv[1:]:
408b8e80941Smrg        basename = os.path.basename(arg)
409b8e80941Smrg        m = re.match(r'(.*)\.h', basename)
410b8e80941Smrg        asic = Asic(m.group(1))
411b8e80941Smrg        with open(arg) as filp:
412b8e80941Smrg            asic.parse(filp, packets, asics)
413b8e80941Smrg        asics.append(asic)
414b8e80941Smrg    write_tables(asics, packets)
415b8e80941Smrg
416b8e80941Smrg
417b8e80941Smrgif __name__ == '__main__':
418b8e80941Smrg    main()
419b8e80941Smrg
420b8e80941Smrg# kate: space-indent on; indent-width 4; replace-tabs on;
421