Simon Glass | 6e58372 | 2019-10-31 07:42:58 -0600 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Simon Glass | b42f7e4 | 2016-01-21 19:45:08 -0700 | [diff] [blame] | 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 | |
| 19 | import csv |
| 20 | import re |
| 21 | import sys |
| 22 | |
| 23 | tab_to_col = 3 |
| 24 | |
| 25 | class 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): |
Simon Glass | 6e58372 | 2019-10-31 07:42:58 -0600 | [diff] [blame] | 46 | print(self) |
| 47 | print() |
Simon Glass | b42f7e4 | 2016-01-21 19:45:08 -0700 | [diff] [blame] | 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 | |
| 54 | class 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): |
Simon Glass | 6e58372 | 2019-10-31 07:42:58 -0600 | [diff] [blame] | 68 | print('/* %s */' % self.name) |
| 69 | print('enum {') |
Simon Glass | b42f7e4 | 2016-01-21 19:45:08 -0700 | [diff] [blame] | 70 | |
| 71 | def output_footer(self): |
Simon Glass | 6e58372 | 2019-10-31 07:42:58 -0600 | [diff] [blame] | 72 | print('};'); |
Simon Glass | b42f7e4 | 2016-01-21 19:45:08 -0700 | [diff] [blame] | 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: |
Simon Glass | 6e58372 | 2019-10-31 07:42:58 -0600 | [diff] [blame] | 100 | print() |
Simon Glass | b42f7e4 | 2016-01-21 19:45:08 -0700 | [diff] [blame] | 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 | |
| 114 | def 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 | |
| 168 | def 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) |
Simon Glass | 6e58372 | 2019-10-31 07:42:58 -0600 | [diff] [blame] | 178 | print('\t%s,' % str) |
Simon Glass | b42f7e4 | 2016-01-21 19:45:08 -0700 | [diff] [blame] | 179 | |
| 180 | # Process a CSV file, e.g. from tabula |
| 181 | def 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 | |
| 204 | fname = sys.argv[1] |
| 205 | name = sys.argv[2] |
| 206 | |
| 207 | # Read output from pdftotext -layout |
| 208 | if 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. |
| 216 | if 0: |
| 217 | with open(fname, 'r') as fd: |
| 218 | process_csv(name, fd) |