tinyriscv/tools/regtool/reggen/bus_interfaces.py

177 lines
6.6 KiB
Python

# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
'''Code representing a list of bus interfaces for a block'''
from typing import Dict, List, Optional, Tuple
from .inter_signal import InterSignal
from .lib import check_list, check_keys, check_str, check_optional_str
class BusInterfaces:
def __init__(self,
has_unnamed_host: bool,
named_hosts: List[str],
has_unnamed_device: bool,
named_devices: List[str]):
assert has_unnamed_device or named_devices
assert len(named_hosts) == len(set(named_hosts))
assert len(named_devices) == len(set(named_devices))
self.has_unnamed_host = has_unnamed_host
self.named_hosts = named_hosts
self.has_unnamed_device = has_unnamed_device
self.named_devices = named_devices
@staticmethod
def from_raw(raw: object, where: str) -> 'BusInterfaces':
has_unnamed_host = False
named_hosts = []
has_unnamed_device = False
named_devices = []
for idx, raw_entry in enumerate(check_list(raw, where)):
entry_what = 'entry {} of {}'.format(idx + 1, where)
ed = check_keys(raw_entry, entry_what,
['protocol', 'direction'],
['name'])
protocol = check_str(ed['protocol'],
'protocol field of ' + entry_what)
if protocol != 'tlul':
raise ValueError('Unknown protocol {!r} at {}'
.format(protocol, entry_what))
direction = check_str(ed['direction'],
'direction field of ' + entry_what)
if direction not in ['device', 'host']:
raise ValueError('Unknown interface direction {!r} at {}'
.format(direction, entry_what))
name = check_optional_str(ed.get('name'),
'name field of ' + entry_what)
if direction == 'host':
if name is None:
if has_unnamed_host:
raise ValueError('Multiple un-named host '
'interfaces at {}'
.format(where))
has_unnamed_host = True
else:
if name in named_hosts:
raise ValueError('Duplicate host interface '
'with name {!r} at {}'
.format(name, where))
named_hosts.append(name)
else:
if name is None:
if has_unnamed_device:
raise ValueError('Multiple un-named device '
'interfaces at {}'
.format(where))
has_unnamed_device = True
else:
if name in named_devices:
raise ValueError('Duplicate device interface '
'with name {!r} at {}'
.format(name, where))
named_devices.append(name)
if not (has_unnamed_device or named_devices):
raise ValueError('No device interface at ' + where)
return BusInterfaces(has_unnamed_host, named_hosts,
has_unnamed_device, named_devices)
def has_host(self) -> bool:
return bool(self.has_unnamed_host or self.named_hosts)
def _interfaces(self) -> List[Tuple[bool, Optional[str]]]:
ret = [] # type: List[Tuple[bool, Optional[str]]]
if self.has_unnamed_host:
ret.append((True, None))
for name in self.named_hosts:
ret.append((True, name))
if self.has_unnamed_device:
ret.append((False, None))
for name in self.named_devices:
ret.append((False, name))
return ret
@staticmethod
def _if_dict(is_host: bool, name: Optional[str]) -> Dict[str, object]:
ret = {
'protocol': 'tlul',
'direction': 'host' if is_host else 'device'
} # type: Dict[str, object]
if name is not None:
ret['name'] = name
return ret
def as_dicts(self) -> List[Dict[str, object]]:
return [BusInterfaces._if_dict(is_host, name)
for is_host, name in self._interfaces()]
def get_port_name(self, is_host: bool, name: Optional[str]) -> str:
if is_host:
tl_suffix = 'tl_h'
else:
tl_suffix = 'tl_d' if self.has_host() else 'tl'
return (tl_suffix if name is None
else '{}_{}'.format(name, tl_suffix))
def get_port_names(self, inc_hosts: bool, inc_devices: bool) -> List[str]:
ret = []
for is_host, name in self._interfaces():
if not (inc_hosts if is_host else inc_devices):
continue
ret.append(self.get_port_name(is_host, name))
return ret
def _if_inter_signal(self,
is_host: bool,
name: Optional[str]) -> InterSignal:
act = 'req' if is_host else 'rsp'
return InterSignal(self.get_port_name(is_host, name),
None, 'tl', 'tlul_pkg', 'req_rsp', act, 1, None)
def inter_signals(self) -> List[InterSignal]:
return [self._if_inter_signal(is_host, name)
for is_host, name in self._interfaces()]
def has_interface(self, is_host: bool, name: Optional[str]) -> bool:
if is_host:
if name is None:
return self.has_unnamed_host
else:
return name in self.named_hosts
else:
if name is None:
return self.has_unnamed_device
else:
return name in self.named_devices
def find_port_name(self, is_host: bool, name: Optional[str]) -> str:
'''Look up the given host/name pair and return its port name.
Raises a KeyError if there is no match.
'''
if not self.has_interface(is_host, name):
called = ('with no name'
if name is None else 'called {!r}'.format(name))
raise KeyError('There is no {} bus interface {}.'
.format('host' if is_host else 'device',
called))
return self.get_port_name(is_host, name)