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 | Register JSON validation |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 6 | """ |
| 7 | |
| 8 | import logging as log |
Rupert Swarbrick | 0f6eeaf | 2021-05-28 15:24:14 +0100 | [diff] [blame] | 9 | from typing import Dict, List, Tuple, Union |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 10 | |
| 11 | |
| 12 | # validating version of int(x, 0) |
| 13 | # returns int value, error flag |
| 14 | # if error flag is True value will be zero |
Rupert Swarbrick | 0f6eeaf | 2021-05-28 15:24:14 +0100 | [diff] [blame] | 15 | def check_int(x: Union[int, str], |
| 16 | err_prefix: str, |
| 17 | suppress_err_msg: bool = False) -> Tuple[int, bool]: |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 18 | if isinstance(x, int): |
| 19 | return x, False |
| 20 | if x[0] == '0' and len(x) > 2: |
| 21 | if x[1] in 'bB': |
| 22 | validch = '01' |
| 23 | elif x[1] in 'oO': |
| 24 | validch = '01234567' |
| 25 | elif x[1] in 'xX': |
| 26 | validch = '0123456789abcdefABCDEF' |
| 27 | else: |
Michael Schaffner | 1b4744e | 2020-04-10 14:59:58 -0700 | [diff] [blame] | 28 | if not suppress_err_msg: |
| 29 | log.error(err_prefix + |
| 30 | ": int must start digit, 0b, 0B, 0o, 0O, 0x or 0X") |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 31 | return 0, True |
| 32 | for c in x[2:]: |
Eunchan Kim | 0bd7566 | 2020-04-24 10:21:18 -0700 | [diff] [blame] | 33 | if c not in validch: |
Michael Schaffner | 1b4744e | 2020-04-10 14:59:58 -0700 | [diff] [blame] | 34 | if not suppress_err_msg: |
| 35 | log.error(err_prefix + ": Bad character " + c + " in " + x) |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 36 | return 0, True |
| 37 | else: |
| 38 | if not x.isdecimal(): |
Michael Schaffner | 1b4744e | 2020-04-10 14:59:58 -0700 | [diff] [blame] | 39 | if not suppress_err_msg: |
| 40 | log.error(err_prefix + ": Number not valid int " + x) |
| 41 | return 0, True |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 42 | return int(x, 0), False |
| 43 | |
| 44 | |
Rupert Swarbrick | 0f6eeaf | 2021-05-28 15:24:14 +0100 | [diff] [blame] | 45 | def check_bool(x: Union[bool, str], err_prefix: str) -> Tuple[bool, bool]: |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 46 | """check_bool checks if input 'x' is one of the list: |
| 47 | "true", "false" |
| 48 | |
| 49 | It returns value as Bool type and Error condition. |
| 50 | """ |
| 51 | if isinstance(x, bool): |
| 52 | # if Bool returns as it is |
| 53 | return x, False |
| 54 | if not x.lower() in ["true", "false"]: |
| 55 | log.error(err_prefix + ": Bad field value " + x) |
| 56 | return False, True |
| 57 | else: |
| 58 | return (x.lower() == "true"), False |
| 59 | |
| 60 | |
Rupert Swarbrick | 0f6eeaf | 2021-05-28 15:24:14 +0100 | [diff] [blame] | 61 | def check_ln(obj: Dict[str, object], |
| 62 | x: str, |
| 63 | withwidth: bool, |
| 64 | err_prefix: str) -> int: |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 65 | error = 0 |
Rupert Swarbrick | 0f6eeaf | 2021-05-28 15:24:14 +0100 | [diff] [blame] | 66 | entry = obj[x] |
| 67 | if not isinstance(entry, list): |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 68 | log.error(err_prefix + ' element ' + x + ' not a list') |
| 69 | return 1 |
Rupert Swarbrick | 0f6eeaf | 2021-05-28 15:24:14 +0100 | [diff] [blame] | 70 | |
| 71 | for y in entry: |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 72 | error += check_keys(y, ln_required, ln_optional if withwidth else {}, |
| 73 | {}, err_prefix + ' element ' + x) |
| 74 | if withwidth: |
| 75 | if 'width' in y: |
| 76 | w, err = check_int(y['width'], err_prefix + ' width in ' + x) |
| 77 | if err: |
| 78 | error += 1 |
| 79 | w = 1 |
| 80 | else: |
| 81 | w = 1 |
| 82 | y['width'] = str(w) |
| 83 | |
| 84 | return error |
| 85 | |
| 86 | |
Rupert Swarbrick | 0f6eeaf | 2021-05-28 15:24:14 +0100 | [diff] [blame] | 87 | def check_keys(obj: Dict[str, object], |
| 88 | required_keys: Dict[str, List[str]], |
| 89 | optional_keys: Dict[str, List[str]], |
| 90 | added_keys: Dict[str, List[str]], |
| 91 | err_prefix: str) -> int: |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 92 | error = 0 |
| 93 | for x in required_keys: |
Eunchan Kim | 0bd7566 | 2020-04-24 10:21:18 -0700 | [diff] [blame] | 94 | if x not in obj: |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 95 | error += 1 |
| 96 | log.error(err_prefix + " missing required key " + x) |
| 97 | for x in obj: |
Rupert Swarbrick | c02b52f | 2020-10-07 15:34:16 +0100 | [diff] [blame] | 98 | type = None |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 99 | if x in required_keys: |
| 100 | type = required_keys[x][0] |
| 101 | elif x in optional_keys: |
| 102 | type = optional_keys[x][0] |
Eunchan Kim | 0bd7566 | 2020-04-24 10:21:18 -0700 | [diff] [blame] | 103 | elif x not in added_keys: |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 104 | log.warning(err_prefix + " contains extra key " + x) |
Rupert Swarbrick | c02b52f | 2020-10-07 15:34:16 +0100 | [diff] [blame] | 105 | if type is not None: |
| 106 | if type[:2] == 'ln': |
| 107 | error += check_ln(obj, x, type == 'lnw', err_prefix) |
Eunchan Kim | c4873f3 | 2019-09-25 12:46:12 -0700 | [diff] [blame] | 108 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 109 | return error |
| 110 | |
| 111 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 112 | val_types = { |
| 113 | 'd': ["int", "integer (binary 0b, octal 0o, decimal, hex 0x)"], |
| 114 | 'x': ["xint", "x for undefined otherwise int"], |
| 115 | 'b': [ |
Eunchan Kim | 0bd7566 | 2020-04-24 10:21:18 -0700 | [diff] [blame] | 116 | "bitrange", "bit number as decimal integer, " |
| 117 | "or bit-range as decimal integers msb:lsb" |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 118 | ], |
| 119 | 'l': ["list", "comma separated list enclosed in `[]`"], |
Eunchan Kim | 0bd7566 | 2020-04-24 10:21:18 -0700 | [diff] [blame] | 120 | 'ln': [ |
| 121 | "name list", 'comma separated list enclosed in `[]` of ' |
| 122 | 'one or more groups that have just name and dscr keys.' |
| 123 | ' e.g. `{ name: "name", desc: "description"}`' |
| 124 | ], |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 125 | 'lnw': ["name list+", 'name list that optionally contains a width'], |
Eunchan Kim | c4873f3 | 2019-09-25 12:46:12 -0700 | [diff] [blame] | 126 | 'lp': ["parameter list", 'parameter list having default value optionally'], |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 127 | 'g': ["group", "comma separated group of key:value enclosed in `{}`"], |
Eunchan Kim | 0bd7566 | 2020-04-24 10:21:18 -0700 | [diff] [blame] | 128 | 'lg': [ |
| 129 | "list of group", "comma separated group of key:value enclosed in `{}`" |
| 130 | " the second entry of the list is the sub group format" |
| 131 | ], |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 132 | 's': ["string", "string, typically short"], |
Eunchan Kim | 0bd7566 | 2020-04-24 10:21:18 -0700 | [diff] [blame] | 133 | 't': [ |
| 134 | "text", "string, may be multi-line enclosed in `'''` " |
| 135 | "may use `**bold**`, `*italic*` or `!!Reg` markup" |
| 136 | ], |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 137 | 'T': ["tuple", "tuple enclosed in ()"], |
Philipp Wagner | 14a3fee | 2019-11-21 10:07:02 +0000 | [diff] [blame] | 138 | 'pi': ["python int", "Native Python type int (generated)"], |
| 139 | 'pb': ["python Bool", "Native Python type Bool (generated)"], |
| 140 | 'pl': ["python list", "Native Python type list (generated)"], |
| 141 | 'pe': ["python enum", "Native Python type enum (generated)"] |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 142 | } |
| 143 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 144 | # ln type has list of groups with only name and description |
| 145 | # (was called "subunit" in cfg_validate) |
| 146 | ln_required = { |
| 147 | 'name': ['s', "name of the item"], |
| 148 | 'desc': ['s', "description of the item"], |
| 149 | } |
| 150 | ln_optional = { |
| 151 | 'width': ['d', "bit width of the item (if not 1)"], |
| 152 | } |
| 153 | |
| 154 | # Registers list may have embedded keys |
Eunchan Kim | 0bd7566 | 2020-04-24 10:21:18 -0700 | [diff] [blame] | 155 | list_optone = { |
| 156 | 'reserved': ['d', "number of registers to reserve space for"], |
| 157 | 'skipto': ['d', "set next register offset to value"], |
Eunchan Kim | 0bd7566 | 2020-04-24 10:21:18 -0700 | [diff] [blame] | 158 | 'window': [ |
| 159 | 'g', "group defining an address range " |
| 160 | "for something other than standard registers" |
| 161 | ], |
| 162 | 'multireg': |
| 163 | ['g', "group defining registers generated " |
| 164 | "from a base instance."] |
| 165 | } |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 166 | |
lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 167 | key_use = {'r': "required", 'o': "optional", 'a': "added by tool"} |