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