lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 1 | # Copyright lowRISC contributors. |
| 2 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | """ |
Philipp Wagner | 14a3fee | 2019-11-21 10:07:02 +0000 | [diff] [blame] | 5 | Generate C header from validated register JSON tree |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 6 | """ |
| 7 | |
| 8 | import io |
| 9 | import logging as log |
| 10 | import re |
| 11 | import sys |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 12 | import textwrap |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 13 | |
| 14 | |
| 15 | def genout(outfile, msg): |
| 16 | outfile.write(msg) |
| 17 | |
| 18 | |
| 19 | def as_define(s): |
| 20 | s = s.upper() |
| 21 | r = '' |
| 22 | for i in range(0, len(s)): |
| 23 | r += s[i] if s[i].isalnum() else '_' |
| 24 | return r |
| 25 | |
| 26 | |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 27 | def first_line(s): |
| 28 | """Returns the first line of a multi-line string""" |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 29 | return s.splitlines()[0] |
| 30 | |
| 31 | |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 32 | def format_comment(s): |
| 33 | """Formats a string to comment wrapped to an 80 character line width |
| 34 | |
| 35 | Returns wrapped string including newline and // comment characters. |
| 36 | """ |
| 37 | return '\n'.join( |
| 38 | textwrap.wrap( |
| 39 | s, width=77, initial_indent='// ', subsequent_indent='// ')) + '\n' |
| 40 | |
| 41 | |
| 42 | def gen_define(name, args, body, indent=' '): |
| 43 | r"""Produces a #define string, will split into two lines if a single line |
| 44 | has a width greater than 80 characters. Result includes newline. |
| 45 | |
| 46 | Arguments: |
| 47 | name - Name of the #define |
| 48 | args - List of arguments for the define, provide an empty list if there are |
| 49 | none |
| 50 | body - Body of the #define |
| 51 | indent - Gives string to prepend on any new lines produced by |
| 52 | wrapping (default ' ') |
| 53 | |
| 54 | Example result: |
| 55 | name = 'A_MACRO' |
| 56 | args = ['arg1', 'arg2'], |
| 57 | body = 'arg1 + arg2 + 10' |
| 58 | |
| 59 | #define A_MACRO(arg1, arg2) arg1 + arg2 + 10 |
| 60 | |
| 61 | When the macro is wrapped the break happens after the argument list (or |
| 62 | macro name if there is no argument list |
| 63 | |
| 64 | #define A_MACRO(arg1, arg2) \ |
| 65 | arg1 + arg2 + 10 |
| 66 | |
| 67 | """ |
| 68 | if len(args) != 0: |
| 69 | define_declare = '#define ' + name + '(' + ', '.join(args) + ')' |
| 70 | else: |
| 71 | define_declare = '#define ' + name |
| 72 | |
| 73 | oneline_define = define_declare + ' ' + body |
| 74 | |
| 75 | if len(oneline_define) <= 80: |
| 76 | return oneline_define + '\n' |
| 77 | |
| 78 | return define_declare + ' \\\n' + indent + body + '\n' |
| 79 | |
| 80 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 81 | def gen_cdefine_register(outstr, reg, comp, width, rnames): |
| 82 | rname = reg['name'] |
| 83 | offset = reg['genoffset'] |
| 84 | |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 85 | genout(outstr, format_comment(first_line(reg['desc']))) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 86 | defname = as_define(comp + '_' + rname) |
| 87 | genout( |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 88 | outstr, |
| 89 | gen_define( |
| 90 | defname, ['id'], |
| 91 | '(' + as_define(comp) + '##id##_BASE_ADDR + ' + hex(offset) + ')')) |
Miguel Young de la Sota | 8d3985b | 2019-12-02 15:51:35 -0600 | [diff] [blame] | 92 | genout(outstr, gen_define(defname + '_REG_OFFSET', [], hex(offset))) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 93 | |
| 94 | for field in reg['fields']: |
| 95 | fieldlsb = field['bitinfo'][2] |
| 96 | fname = field['name'] |
| 97 | if fname == rname: |
| 98 | dname = defname |
| 99 | else: |
| 100 | dname = defname + '_' + as_define(fname) |
| 101 | |
| 102 | if field['bitinfo'][1] == 1: |
| 103 | # single bit |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 104 | genout(outstr, gen_define(dname, [], str(fieldlsb))) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 105 | else: |
| 106 | # multiple bits (unless it is the whole register) |
| 107 | if field['bitinfo'][1] != width: |
| 108 | mask = field['bitinfo'][0] >> fieldlsb |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 109 | genout(outstr, gen_define(dname + '_MASK', [], hex(mask))) |
| 110 | genout(outstr, gen_define(dname + '_OFFSET', [], |
| 111 | str(fieldlsb))) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 112 | if 'enum' in field: |
| 113 | for enum in field['enum']: |
| 114 | ename = as_define(enum['name']) |
| 115 | genout( |
| 116 | outstr, |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 117 | gen_define( |
| 118 | defname + '_' + as_define(field['name']) + '_' + |
| 119 | ename, [], enum['value'])) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 120 | genout(outstr, '\n') |
| 121 | return |
| 122 | |
| 123 | |
| 124 | def gen_cdefine_window(outstr, win, comp, regwidth, rnames): |
| 125 | wname = win['name'] |
| 126 | offset = win['genoffset'] |
| 127 | |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 128 | genout(outstr, format_comment('Memory area: ' + first_line(win['desc']))) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 129 | defname = as_define(comp + '_' + wname) |
| 130 | genout( |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 131 | outstr, |
| 132 | gen_define( |
| 133 | defname, ['id'], |
| 134 | '(' + as_define(comp) + '##id##_BASE_ADDR + ' + hex(offset) + ')')) |
Miguel Young de la Sota | 8d3985b | 2019-12-02 15:51:35 -0600 | [diff] [blame] | 135 | genout(outstr, gen_define(defname + '_REG_OFFSET', [], hex(offset))) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 136 | items = int(win['items']) |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 137 | genout(outstr, gen_define(defname + '_SIZE_WORDS', [], str(items))) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 138 | items = items * (regwidth // 8) |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 139 | genout(outstr, gen_define(defname + '_SIZE_BYTES', [], str(items))) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 140 | |
| 141 | wid = win['genvalidbits'] |
| 142 | if (wid != regwidth): |
| 143 | mask = (1 << wid) - 1 |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 144 | genout(outstr, gen_define(defname + '_MASK ', [], hex(mask))) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 145 | |
| 146 | |
| 147 | # Must have called validate, so should have no errors |
| 148 | |
| 149 | |
| 150 | def gen_cdefines(regs, outfile, src_lic, src_copy): |
| 151 | component = regs['name'] |
| 152 | registers = regs['registers'] |
| 153 | rnames = regs['genrnames'] |
| 154 | outstr = io.StringIO() |
| 155 | |
| 156 | if 'regwidth' in regs: |
| 157 | regwidth = int(regs['regwidth'], 0) |
| 158 | else: |
| 159 | regwidth = 32 |
| 160 | |
| 161 | for x in registers: |
| 162 | if 'reserved' in x: |
| 163 | continue |
| 164 | |
| 165 | if 'skipto' in x: |
| 166 | continue |
| 167 | |
| 168 | if 'sameaddr' in x: |
| 169 | for sareg in x['sameaddr']: |
| 170 | gen_cdefine_register(outstr, sareg, component, regwidth, |
| 171 | rnames) |
| 172 | continue |
| 173 | |
| 174 | if 'window' in x: |
| 175 | gen_cdefine_window(outstr, x['window'], component, regwidth, |
| 176 | rnames) |
| 177 | continue |
| 178 | |
| 179 | if 'multireg' in x: |
| 180 | for reg in x['multireg']['genregs']: |
| 181 | gen_cdefine_register(outstr, reg, component, regwidth, rnames) |
| 182 | continue |
| 183 | |
| 184 | gen_cdefine_register(outstr, x, component, regwidth, rnames) |
| 185 | |
| 186 | generated = outstr.getvalue() |
| 187 | outstr.close() |
| 188 | |
| 189 | genout(outfile, '// Generated register defines for ' + component + '\n\n') |
| 190 | if src_copy != '': |
| 191 | genout(outfile, '// Copyright information found in source file:\n') |
| 192 | genout(outfile, '// ' + src_copy + '\n\n') |
| 193 | if src_lic != None: |
| 194 | genout(outfile, '// Licensing information found in source file:\n') |
| 195 | for line in src_lic.splitlines(): |
| 196 | genout(outfile, '// ' + line + '\n') |
| 197 | genout(outfile, '\n') |
| 198 | genout(outfile, '#ifndef _' + as_define(component) + '_REG_DEFS_\n') |
| 199 | genout(outfile, '#define _' + as_define(component) + '_REG_DEFS_\n\n') |
| 200 | genout(outfile, generated) |
| 201 | genout(outfile, '#endif // _' + as_define(component) + '_REG_DEFS_\n') |
| 202 | genout(outfile, '// End generated register defines for ' + component) |
| 203 | |
| 204 | return |
Greg Chadwick | 45859b7 | 2019-10-23 13:06:29 +0100 | [diff] [blame] | 205 | |
| 206 | |
| 207 | def test_gen_define(): |
| 208 | basic_oneline = '#define MACRO_NAME body\n' |
| 209 | assert gen_define('MACRO_NAME', [], 'body') == basic_oneline |
| 210 | |
| 211 | basic_oneline_with_args = '#define MACRO_NAME(arg1, arg2) arg1 + arg2\n' |
| 212 | assert (gen_define('MACRO_NAME', ['arg1', 'arg2'], |
| 213 | 'arg1 + arg2') == basic_oneline_with_args) |
| 214 | |
| 215 | long_macro_name = 'A_VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_VERY_LONG_MACRO_NAME' |
| 216 | |
| 217 | multiline = ('#define ' + long_macro_name + ' \\\n' + |
| 218 | ' a_fairly_long_body + something_else + 10\n') |
| 219 | |
| 220 | assert (gen_define( |
| 221 | long_macro_name, [], |
| 222 | 'a_fairly_long_body + something_else + 10') == multiline) |
| 223 | |
| 224 | multiline_with_args = ('#define ' + long_macro_name + |
| 225 | '(arg1, arg2, arg3) \\\n' + |
| 226 | ' a_fairly_long_body + arg1 + arg2 + arg3\n') |
| 227 | |
| 228 | assert (gen_define( |
| 229 | long_macro_name, ['arg1', 'arg2', 'arg3'], |
| 230 | 'a_fairly_long_body + arg1 + arg2 + arg3') == multiline_with_args) |
| 231 | |
| 232 | multiline_with_args_big_indent = ( |
| 233 | '#define ' + long_macro_name + '(arg1, arg2, arg3) \\\n' + |
| 234 | ' a_fairly_long_body + arg1 + arg2 + arg3\n') |
| 235 | |
| 236 | assert (gen_define(long_macro_name, ['arg1', 'arg2', 'arg3'], |
| 237 | 'a_fairly_long_body + arg1 + arg2 + arg3', |
| 238 | indent=' ') == multiline_with_args_big_indent) |