|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | from typing import Dict, Optional | 
|  | from .clocks import Clocks | 
|  |  | 
|  |  | 
|  | class ResetItem: | 
|  | '''Individual resets''' | 
|  | def __init__(self, hier: Dict[str, str], raw: Dict[str, object], clocks: Clocks): | 
|  | if not raw['name']: | 
|  | raise ValueError('Reset has no name') | 
|  |  | 
|  | self.name = raw['name'] | 
|  | self.gen = raw.get('gen', True) | 
|  | self.rst_type = raw.get('type', 'top') | 
|  |  | 
|  | self.path = "" | 
|  | if self.rst_type == 'top': | 
|  | self.path = f"{hier['top']}rst_{self.name}_n" | 
|  | self.lpg_path = f"{hier['lpg']}{self.name}" | 
|  | elif self.rst_type == 'ext': | 
|  | self.path = f"{hier['ext']}{self.name}" | 
|  |  | 
|  | self.shadow_path = "" | 
|  | if self.rst_type == 'top': | 
|  | self.shadow_path = f"{hier['top']}rst_{self.name}_shadowed_n" | 
|  | self.shadow_lpg_path = f"{hier['lpg']}{self.name}_shadowed" | 
|  |  | 
|  | # to be constructed later | 
|  | self.domains = [] | 
|  | self.shadowed = False | 
|  |  | 
|  | self.parent = raw.get('parent', "") | 
|  |  | 
|  | # This can be a source clock or a derived source | 
|  | if self.rst_type != 'ext': | 
|  | self.clock = clocks.get_clock_by_name(raw['clk']) | 
|  | else: | 
|  | self.clock = None | 
|  |  | 
|  | self.sw = bool(raw.get('sw', 0)) | 
|  |  | 
|  | def _asdict(self) -> Dict[str, object]: | 
|  | ret = { | 
|  | 'name': self.name, | 
|  | 'gen': self.gen, | 
|  | 'type': self.rst_type, | 
|  | 'domains': self.domains, | 
|  | 'shadowed': self.shadowed, | 
|  | 'sw': self.sw, | 
|  | 'path': self.path | 
|  | } | 
|  |  | 
|  | if self.parent: | 
|  | ret['parent'] = self.parent | 
|  |  | 
|  | if self.clock: | 
|  | ret['clock'] = self.clock.name | 
|  |  | 
|  | return ret | 
|  |  | 
|  |  | 
|  | class Resets: | 
|  | '''Resets for the chip''' | 
|  | def __init__(self, raw: Dict[str, object], clocks: Clocks): | 
|  | self.hier_paths = {} | 
|  | assert isinstance(raw['hier_paths'], dict) | 
|  | for rst_src, path in raw['hier_paths'].items(): | 
|  | self.hier_paths[str(rst_src)] = str(path) | 
|  |  | 
|  | assert isinstance(raw['nodes'], list) | 
|  |  | 
|  | self.nodes = {} | 
|  | for node in raw['nodes']: | 
|  | assert isinstance(node, dict) | 
|  | reset = ResetItem(self.hier_paths, node, clocks) | 
|  | self.nodes[reset.name] = reset | 
|  |  | 
|  | def _asdict(self) -> Dict[str, object]: | 
|  | ret = { | 
|  | 'hier_paths': self.hier_paths, | 
|  | 'nodes': list(self.nodes.values()) | 
|  | } | 
|  |  | 
|  | return ret | 
|  |  | 
|  | def get_reset_by_name(self, name: str) -> ResetItem: | 
|  |  | 
|  | ret = self.nodes.get(name, None) | 
|  | if ret: | 
|  | return ret | 
|  | else: | 
|  | raise ValueError(f'{name} is not a defined reset') | 
|  |  | 
|  | def mark_reset_shadowed(self, name: str): | 
|  | '''Mark particular reset as requiring shadow''' | 
|  |  | 
|  | reset = self.get_reset_by_name(name) | 
|  | reset.shadowed = True | 
|  |  | 
|  | def get_reset_domains(self, name: str): | 
|  | '''Get available domains for a reset''' | 
|  |  | 
|  | return self.get_reset_by_name(name).domains | 
|  |  | 
|  | def get_clocks(self) -> list: | 
|  | '''Get associated clocks''' | 
|  |  | 
|  | clocks = {} | 
|  | for reset in self.nodes.values(): | 
|  | if reset.rst_type != 'ext': | 
|  | clocks[reset.clock.name] = 1 | 
|  |  | 
|  | return list(clocks) | 
|  |  | 
|  | def get_generated_resets(self) -> list: | 
|  | '''Get generated resets and return reset object | 
|  | ''' | 
|  | return [reset | 
|  | for reset in self.nodes.values() | 
|  | if reset.gen] | 
|  |  | 
|  | def get_top_resets(self) -> list: | 
|  | '''Get resets pushed to the top level''' | 
|  |  | 
|  | return [reset | 
|  | for reset in self.nodes.values() | 
|  | if reset.rst_type == 'top'] | 
|  |  | 
|  | def get_sw_resets(self) -> list: | 
|  | '''Get software controlled resets''' | 
|  |  | 
|  | return [reset.name | 
|  | for reset in self.nodes.values() | 
|  | if reset.sw] | 
|  |  | 
|  | def get_path(self, name: str, domain: Optional[str], shadow = False) -> str: | 
|  | '''Get path to reset''' | 
|  |  | 
|  | reset = self.get_reset_by_name(name) | 
|  | if reset.rst_type == 'int': | 
|  | raise ValueError(f'Reset {name} is not a reset exported from rstmgr') | 
|  |  | 
|  | if reset.rst_type == 'ext': | 
|  | return reset.path | 
|  |  | 
|  | if shadow: | 
|  | path = reset.shadow_path | 
|  | else: | 
|  | path = reset.path | 
|  |  | 
|  | if domain: | 
|  | path += f'[rstmgr_pkg::Domain{domain}Sel]' | 
|  |  | 
|  | return path | 
|  |  | 
|  | def get_lpg_path(self, name: str, domain: Optional[str], shadow = False) -> str: | 
|  | '''Get path to lpg indication signals''' | 
|  |  | 
|  | reset = self.get_reset_by_name(name) | 
|  | if reset.rst_type == 'int': | 
|  | raise ValueError(f'Reset {name} is not a reset exported from rstmgr') | 
|  |  | 
|  | if reset.rst_type == 'ext': | 
|  | raise ValueError(f'External reset {name} cannot be associated with an LPG') | 
|  |  | 
|  | if shadow: | 
|  | path = reset.shadow_lpg_path | 
|  | else: | 
|  | path = reset.lpg_path | 
|  |  | 
|  | if domain: | 
|  | path += f'[rstmgr_pkg::Domain{domain}Sel]' | 
|  |  | 
|  | return path | 
|  |  | 
|  | def get_unused_resets(self, domains: list) -> Dict[str, str]: | 
|  | '''Get unused resets''' | 
|  |  | 
|  | top_resets = [reset | 
|  | for reset in self.nodes.values() | 
|  | if reset.rst_type == 'top'] | 
|  |  | 
|  | ret = {} | 
|  | for reset in top_resets: | 
|  | for dom in domains: | 
|  | if dom not in reset.domains: | 
|  | doml = dom.lower() | 
|  | ret[f'unused_d{doml}_rst_{reset.name}'] = \ | 
|  | f'{reset.path}[rstmgr_pkg::Domain{dom}Sel]' | 
|  |  | 
|  | if reset.shadowed: | 
|  | ret[f'unused_d{doml}_rst_{reset.name}_shadowed'] = \ | 
|  | f'{reset.shadow_path}[rstmgr_pkg::Domain{dom}Sel]' | 
|  |  | 
|  | return ret | 
|  |  | 
|  | def add_reset_domain(self, name: str, domain: str): | 
|  | '''Mark particular reset as requiring shadow''' | 
|  |  | 
|  | reset = self.get_reset_by_name(name) | 
|  |  | 
|  | # Other reset types of hardwired domains | 
|  | if reset.rst_type == 'top': | 
|  | if domain not in reset.domains: | 
|  | reset.domains.append(domain) | 
|  |  | 
|  | def has_shadowed_reset(self) -> bool: | 
|  | '''Do any generated resets have a shadow version?''' | 
|  |  | 
|  | for reset in self.nodes.values(): | 
|  | if reset.shadowed: | 
|  | return True | 
|  |  | 
|  | return False |