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