# 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)