|  | # 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"} |