blob: 9a8bda8ba3212292edabaf40d532294ddea8c8d5 [file] [log] [blame]
# 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:
ret[f'rst_{reset.name}_n[{domains.index(dom)}]'] = \
f'{reset.path}[rstmgr_pkg::Domain{dom}Sel]'
if reset.shadowed:
ret[f'rst_{reset.name}_shadowed_n[{domains.index(dom)}]'] = \
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