177 lines
6.6 KiB
Python
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)
|