| # Copyright lowRISC contributors. |
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| # SPDX-License-Identifier: Apache-2.0 |
| """ |
| Register JSON validation |
| """ |
| |
| import logging as log |
| from typing import Dict, List, Tuple, Union |
| |
| |
| # validating version of int(x, 0) |
| # returns int value, error flag |
| # if error flag is True value will be zero |
| def check_int(x: Union[int, str], |
| err_prefix: str, |
| suppress_err_msg: bool = False) -> Tuple[int, bool]: |
| if isinstance(x, int): |
| return x, False |
| if x[0] == '0' and len(x) > 2: |
| if x[1] in 'bB': |
| validch = '01' |
| elif x[1] in 'oO': |
| validch = '01234567' |
| elif x[1] in 'xX': |
| validch = '0123456789abcdefABCDEF' |
| else: |
| if not suppress_err_msg: |
| log.error(err_prefix + |
| ": int must start digit, 0b, 0B, 0o, 0O, 0x or 0X") |
| return 0, True |
| for c in x[2:]: |
| if c not in validch: |
| if not suppress_err_msg: |
| log.error(err_prefix + ": Bad character " + c + " in " + x) |
| return 0, True |
| else: |
| if not x.isdecimal(): |
| if not suppress_err_msg: |
| log.error(err_prefix + ": Number not valid int " + x) |
| return 0, True |
| return int(x, 0), False |
| |
| |
| def check_bool(x: Union[bool, str], err_prefix: str) -> Tuple[bool, bool]: |
| """check_bool checks if input 'x' is one of the list: |
| "true", "false" |
| |
| It returns value as Bool type and Error condition. |
| """ |
| if isinstance(x, bool): |
| # if Bool returns as it is |
| return x, False |
| if not x.lower() in ["true", "false"]: |
| log.error(err_prefix + ": Bad field value " + x) |
| return False, True |
| else: |
| return (x.lower() == "true"), False |
| |
| |
| def check_ln(obj: Dict[str, object], |
| x: str, |
| withwidth: bool, |
| err_prefix: str) -> int: |
| error = 0 |
| entry = obj[x] |
| if not isinstance(entry, list): |
| log.error(err_prefix + ' element ' + x + ' not a list') |
| return 1 |
| |
| for y in entry: |
| error += check_keys(y, ln_required, ln_optional if withwidth else {}, |
| {}, err_prefix + ' element ' + x) |
| if withwidth: |
| if 'width' in y: |
| w, err = check_int(y['width'], err_prefix + ' width in ' + x) |
| if err: |
| error += 1 |
| w = 1 |
| else: |
| w = 1 |
| y['width'] = str(w) |
| |
| return error |
| |
| |
| def check_keys(obj: Dict[str, object], |
| required_keys: Dict[str, List[str]], |
| optional_keys: Dict[str, List[str]], |
| added_keys: Dict[str, List[str]], |
| err_prefix: str) -> int: |
| error = 0 |
| for x in required_keys: |
| if x not in obj: |
| error += 1 |
| log.error(err_prefix + " missing required key " + x) |
| for x in obj: |
| type = None |
| if x in required_keys: |
| type = required_keys[x][0] |
| elif x in optional_keys: |
| type = optional_keys[x][0] |
| elif x not in added_keys: |
| log.warning(err_prefix + " contains extra key " + x) |
| if type is not None: |
| if type[:2] == 'ln': |
| error += check_ln(obj, x, type == 'lnw', err_prefix) |
| |
| return error |
| |
| |
| val_types = { |
| 'd': ["int", "integer (binary 0b, octal 0o, decimal, hex 0x)"], |
| 'x': ["xint", "x for undefined otherwise int"], |
| 'b': [ |
| "bitrange", "bit number as decimal integer, " |
| "or bit-range as decimal integers msb:lsb" |
| ], |
| 'l': ["list", "comma separated list enclosed in `[]`"], |
| 'ln': [ |
| "name list", 'comma separated list enclosed in `[]` of ' |
| 'one or more groups that have just name and dscr keys.' |
| ' e.g. `{ name: "name", desc: "description"}`' |
| ], |
| 'lnw': ["name list+", 'name list that optionally contains a width'], |
| 'lp': ["parameter list", 'parameter list having default value optionally'], |
| 'g': ["group", "comma separated group of key:value enclosed in `{}`"], |
| 'lg': [ |
| "list of group", "comma separated group of key:value enclosed in `{}`" |
| " the second entry of the list is the sub group format" |
| ], |
| 's': ["string", "string, typically short"], |
| 't': [ |
| "text", "string, may be multi-line enclosed in `'''` " |
| "may use `**bold**`, `*italic*` or `!!Reg` markup" |
| ], |
| 'T': ["tuple", "tuple enclosed in ()"], |
| 'pi': ["python int", "Native Python type int (generated)"], |
| 'pb': ["python Bool", "Native Python type Bool (generated)"], |
| 'pl': ["python list", "Native Python type list (generated)"], |
| 'pe': ["python enum", "Native Python type enum (generated)"] |
| } |
| |
| # ln type has list of groups with only name and description |
| # (was called "subunit" in cfg_validate) |
| ln_required = { |
| 'name': ['s', "name of the item"], |
| 'desc': ['s', "description of the item"], |
| } |
| ln_optional = { |
| 'width': ['d', "bit width of the item (if not 1)"], |
| } |
| |
| # Registers list may have embedded keys |
| list_optone = { |
| 'reserved': ['d', "number of registers to reserve space for"], |
| 'skipto': ['d', "set next register offset to value"], |
| 'window': [ |
| 'g', "group defining an address range " |
| "for something other than standard registers" |
| ], |
| 'multireg': |
| ['g', "group defining registers generated " |
| "from a base instance."] |
| } |
| |
| key_use = {'r': "required", 'o': "optional", 'a': "added by tool"} |