tinyriscv/tools/regtool/reggen/validate.py

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