168 lines
5.4 KiB
Python
168 lines
5.4 KiB
Python
|
# 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"}
|