1#!/usr/bin/env python3
2
3# Script to create enums from datasheet register tables
4#
5# Usage:
6#
7# First, create a text file from the datasheet:
8#    pdftotext -layout /path/to/rockchip-3288-trm.pdf /tmp/asc
9#
10# Then use this script to output the #defines for a particular register:
11#    ./tools/rkmux.py GRF_GPIO4C_IOMUX
12#
13# It will create output suitable for putting in a header file, with SHIFT and
14# MASK values for each bitfield in the register.
15#
16# Note: this tool is not perfect and you may need to edit the resulting code.
17# But it should speed up the process.
18
19import csv
20import re
21import sys
22
23tab_to_col = 3
24
25class RegField:
26    def __init__(self, cols=None):
27        if cols:
28            self.bits, self.attr, self.reset_val, self.desc = (
29                [x.strip() for x in cols])
30            self.desc = [self.desc]
31        else:
32            self.bits = ''
33            self.attr = ''
34            self.reset_val = ''
35            self.desc = []
36
37    def Setup(self, cols):
38        self.bits, self.attr, self.reset_val = cols[0:3]
39        if len(cols) > 3:
40            self.desc.append(cols[3])
41
42    def AddDesc(self, desc):
43        self.desc.append(desc)
44
45    def Show(self):
46        print(self)
47        print()
48        self.__init__()
49
50    def __str__(self):
51        return '%s,%s,%s,%s' % (self.bits, self.attr, self.reset_val,
52                                '\n'.join(self.desc))
53
54class Printer:
55    def __init__(self, name):
56        self.first = True
57        self.name = name
58        self.re_sel = re.compile("[1-9]'b([01]+): (.*)")
59
60    def __enter__(self):
61        return self
62
63    def __exit__(self, type, value, traceback):
64        if not self.first:
65            self.output_footer()
66
67    def output_header(self):
68        print('/* %s */' % self.name)
69        print('enum {')
70
71    def output_footer(self):
72        print('};');
73
74    def output_regfield(self, regfield):
75        lines = regfield.desc
76        field = lines[0]
77        #print 'field:', field
78        if field in ['reserved', 'reserve', 'write_enable', 'write_mask']:
79            return
80        if field.endswith('_sel') or field.endswith('_con'):
81            field = field[:-4]
82        elif field.endswith(' iomux'):
83            field = field[:-6]
84        elif field.endswith('_mode') or field.endswith('_mask'):
85            field = field[:-5]
86        #else:
87            #print 'bad field %s' % field
88            #return
89        field = field.upper()
90        if ':' in regfield.bits:
91            bit_high, bit_low = [int(x) for x in regfield.bits.split(':')]
92        else:
93            bit_high = bit_low = int(regfield.bits)
94        bit_width = bit_high - bit_low + 1
95        mask = (1 << bit_width) - 1
96        if self.first:
97            self.first = False
98            self.output_header()
99        else:
100            print()
101        out_enum(field, 'shift', bit_low)
102        out_enum(field, 'mask', mask)
103        next_val = -1
104        #print 'lines: %s', lines
105        for line in lines:
106            m = self.re_sel.match(line)
107            if m:
108                val, enum = int(m.group(1), 2), m.group(2)
109                if enum not in ['reserved', 'reserve']:
110                    out_enum(field, enum, val, val == next_val)
111                    next_val = val + 1
112
113
114def process_file(name, fd):
115    field = RegField()
116    reg = ''
117
118    fields = []
119
120    def add_it(field):
121        if field.bits:
122            if reg == name:
123                fields.append(field)
124            field = RegField()
125        return field
126
127    def is_field_start(line):
128       if '=' in line or '+' in line:
129           return False
130       if (line.startswith('gpio') or line.startswith('peri_') or
131                line.endswith('_sel') or line.endswith('_con')):
132           return True
133       if not ' ' in line: # and '_' in line:
134           return True
135       return False
136
137    for line in fd:
138        line = line.rstrip()
139        if line[:4] in ['GRF_', 'PMU_', 'CRU_']:
140            field = add_it(field)
141            reg = line
142            do_this = name == reg
143        elif not line or not line.startswith(' '):
144            continue
145        line = line.replace('\xe2\x80\x99', "'")
146        leading = len(line) - len(line.lstrip())
147        line = line.lstrip()
148        cols = re.split(' *', line, 3)
149        if leading > 15 or (len(cols) > 3 and is_field_start(cols[3])):
150            if is_field_start(line):
151                field = add_it(field)
152            field.AddDesc(line)
153        else:
154            if cols[0] == 'Bit' or len(cols) < 3:
155                continue
156            #print
157            #print field
158            field = add_it(field)
159            field.Setup(cols)
160    field = add_it(field)
161
162    with Printer(name) as printer:
163        for field in fields:
164            #print field
165            printer.output_regfield(field)
166            #print
167
168def out_enum(field, suffix, value, skip_val=False):
169    str = '%s_%s' % (field.upper(), suffix.upper())
170    if not skip_val:
171        tabs = tab_to_col - len(str) / 8
172        if value > 9:
173            val_str = '%#x' % value
174        else:
175            val_str = '%d' % value
176
177        str += '%s= %s' % ('\t' * tabs, val_str)
178    print('\t%s,' % str)
179
180# Process a CSV file, e.g. from tabula
181def process_csv(name, fd):
182    reader = csv.reader(fd)
183
184    rows = []
185
186    field = RegField()
187    for row in reader:
188        #print field.desc
189        if not row[0]:
190            field.desc.append(row[3])
191            continue
192        if field.bits:
193            if field.bits != 'Bit':
194                rows.append(field)
195        #print row
196        field = RegField(row)
197
198    with Printer(name) as printer:
199        for row in rows:
200            #print field
201            printer.output_regfield(row)
202            #print
203
204fname = sys.argv[1]
205name = sys.argv[2]
206
207# Read output from pdftotext -layout
208if 1:
209    with open(fname, 'r') as fd:
210        process_file(name, fd)
211
212# Use tabula
213# It seems to be better at outputting text for an entire cell in one cell.
214# But it does not always work. E.g. GRF_GPIO7CH_IOMUX.
215# So there is no point in using it.
216if 0:
217    with open(fname, 'r') as fd:
218        process_csv(name, fd)
219