blob: ce232f4b80b6bcf1ab0bb9339f216168de6f92d8 [file] [log] [blame]
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
import logging as log
from collections import OrderedDict
from functools import partial
from .item import Edge, Node, NodeType
from .xbar import Xbar
from reggen.validate import val_types, check_int, check_bool, check_ln
# 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)"]
# }
node = {
'name': 'Node configuration',
'description': '''
Crossbar node description. It can be host, device, or internal nodes.
''',
'required': {
'name': ['s', 'Module instance name'],
'type': ['s', 'Module type: {"host", "device", "async", "socket_1n", "socket_m1"}'],
},
'optional': {
'clock': ['s', 'main clock of the port'],
'base_addr': ['d', 'Base address of the device'\
' It is required for the device'],
'size_byte': ['d', 'Memory space of the device'\
' It is required for the device'],
'pipeline': ['pb', 'If true, pipeline is added in front of the port'],
'pipeline_byp': ['pb', 'Pipeline bypass. If true, '\
'request/response are not latched']
},
'added': {}
}
root = {
'name': 'Top configuration',
'description': '''
Crossbar configuration format.
''',
'required': {
'name': ['s', 'Name of the crossbar'],
'clock': ['s', 'Main clock. Internal components use this clock.'\
' If not specified, it is assumed to be in main clock domain'],
'connections':
['g', "List of edge. Key is host, entry in value list is device"],
'nodes': ['lg', node]
},
'optional': {
'type': ['s', 'Indicate hjson type. "xbar" always if exist']
},
'added': {
'reset_connections': ['g', "Generated by topgen. Key is the reset signal inside IP"\
" and value is the top reset signal"],
}
}
def check_keys(obj, control, prefix=""):
""" Check the keys recursively.
The control parameter is a control group to check obj data structure.
"""
error = 0
# required
for k, v in control["required"].items():
if not k in obj:
error += 1
log.error(prefix + " missing required key " + k)
# Check every fields' correctness
for k, v in obj.items():
checker = ['', '']
prefix_name = prefix + " " + k
if k in control["required"]:
checker = control["required"][k]
elif k in control["optional"]:
checker = control["optional"][k]
elif k in control["added"]:
log.warning(prefix + " contains generated key " + k)
checker = control["added"][k]
else:
log.warning(prefix + " contains extra key " + k)
continue
# Type and value check
if not checker[0] in val_types:
log.error(prefix +
" field {} is undefined type. Check val_types {}".format(
k, checker[0]))
if checker[0] == 'lg':
# List of subgroup
error += sum(
map(
partial(check_keys, control=checker[1],
prefix=prefix_name), obj[k]))
elif checker[0] == 'g':
# if second entry isn't string type, call recursively
if isinstance(checker[1], str):
log.info(
"Skipping {} as no further control group is given".format(
prefix_name))
continue
error += check_keys(obj=obj[k],
control=checker[1],
prefix=prefix_name)
elif checker[0] == 'd':
int_v, err = check_int(obj[k], prefix_name)
if err:
error += 1
elif checker[0] == 's' or checker[0] == 't':
# don't care the string
pass
elif checker[0] == 'pb':
b_v, err = check_bool(obj[k], prefix_name)
if err:
error += 1
else:
log.error(prefix_name +
" is not supported in this configuration format")
return error
def get_nodetype(t): # t: str -> NodeType
if t == "host":
return NodeType.HOST
elif t == "device":
return NodeType.DEVICE
elif t == "async_fifo":
return NodeType.ASYNC_FIFO
elif t == "socket_1n":
return NodeType.SOCKET_1N
elif t == "socket_m1":
return NodeType.SOCKET_M1
raise
def checkNameExist(name, xbar): # name: str -> xbar: Xbar -> bool
return name.lower() in [x.name for x in xbar.nodes]
def isOverlap(range1, range2): # Tuple[int,int] -> Tuple[int,int] -> bool
return not (range2[1] < range1[0] or range2[0] > range1[1])
# Tuple[int,int] -> List[Tuple[]] -> bool
def checkAddressOverlap(addr, ranges):
result = [x for x in ranges if isOverlap(x, addr)]
return len(result) != 0
def validate(obj): # OrderedDict -> Xbar
xbar = Xbar()
xbar.name = obj["name"].lower()
xbar.clock = obj["clock"].lower()
addr_ranges = []
obj, err = validate_hjson(obj) # validate hjson format first
if err > 0:
log.error("Hjson structure error")
return
# Nodes
for nodeobj in obj["nodes"]:
clock = nodeobj["clock"].lower() if "clock" in nodeobj.keys(
) else xbar.clock
if checkNameExist(nodeobj["name"], xbar):
log.error("Duplicated name: %s" % (nodeobj["name"]))
raise SystemExit("Duplicated name in the configuration")
node = Node(name=nodeobj["name"].lower(),
node_type=get_nodetype(nodeobj["type"].lower()),
clock=clock)
if node.node_type == NodeType.DEVICE:
# Add address obj["base_addr"], obj["size"])
node.address_from = int(nodeobj["base_addr"], 0)
size = int(nodeobj["size_byte"], 0)
node.address_to = node.address_from + size - 1
addr = (node.address_from, node.address_to)
if checkAddressOverlap(addr, addr_ranges):
log.error(
"Address is overlapping. Check the config. Addr(0x%x - 0x%x)"
% (addr[0], addr[1]))
raise SystemExit("Address overlapping error occurred")
addr_ranges.append(addr)
if node.node_type in [NodeType.DEVICE, NodeType.HOST
] and "pipeline" in nodeobj:
node.pipeline = True if nodeobj["pipeline"].lower() in [
"true", "1"
] else False
else:
node.pipeline = False
if node.node_type in [NodeType.DEVICE, NodeType.HOST
] and "pipeline_byp" in nodeobj:
node.pipeline_byp = True if nodeobj["pipeline_byp"].lower() in [
"true", "1"
] else False
else:
node.pipeline_byp = True
xbar.nodes.append(node)
# Edge
for host in obj["connections"].keys():
# host: [device]
for device in obj["connections"][host]:
xbar.connect_nodes(host.lower(), device.lower())
return xbar
def validate_hjson(obj):
if not "type" in obj:
obj["type"] = "xbar"
if not "name" in obj:
log.error("Component has no name. Aborting.")
return None, 1
component = obj["name"]
error = check_keys(obj, root, component)
if error > 0:
log.error("{} has top level error. Aborting".format(component))
return None, error
return obj, 0