blob: 5e3a2cd9148ba01f272ef837c03c3443b9ddbd02 [file] [log] [blame]
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +01001# Copyright lowRISC contributors.
2# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3# SPDX-License-Identifier: Apache-2.0
4
5'''Code representing clocking or resets for an IP block'''
6
7from typing import Dict, List, Optional
Timothy Chena49ceb62021-07-13 14:59:09 -07008import re
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +01009
Eunchan Kim2861a7c2022-07-29 11:52:36 -070010from reggen.lib import check_keys, check_list, check_bool, check_optional_name
11
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010012
13class ClockingItem:
Rupert Swarbrick5aa249f2021-07-19 18:35:11 +010014 def __init__(self,
15 clock: Optional[str],
16 reset: Optional[str],
17 idle: Optional[str],
Timothy Chena49ceb62021-07-13 14:59:09 -070018 primary: bool,
Timothy Chen1d0a8172021-09-28 13:40:51 -070019 internal: bool,
Timothy Chena49ceb62021-07-13 14:59:09 -070020 clock_base_name: Optional[str]):
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010021 if primary:
22 assert clock is not None
23 assert reset is not None
24
25 self.clock = clock
Timothy Chena49ceb62021-07-13 14:59:09 -070026 self.clock_base_name = clock_base_name
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010027 self.reset = reset
28 self.primary = primary
Rupert Swarbrick5aa249f2021-07-19 18:35:11 +010029 self.idle = idle
Timothy Chen1d0a8172021-09-28 13:40:51 -070030 # Internal means this clock is generated completely internal to the module
31 # and not supplied by the top level.
32 # However, the IpBlock may need to be aware of this clock for CDC purposes
33 self.internal = internal
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010034
35 @staticmethod
36 def from_raw(raw: object, only_item: bool, where: str) -> 'ClockingItem':
37 what = f'clocking item at {where}'
Timothy Chen1d0a8172021-09-28 13:40:51 -070038 rd = check_keys(raw, what, [], ['clock', 'reset', 'idle', 'primary', 'internal'])
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010039
40 clock = check_optional_name(rd.get('clock'), 'clock field of ' + what)
41 reset = check_optional_name(rd.get('reset'), 'reset field of ' + what)
Rupert Swarbrick5aa249f2021-07-19 18:35:11 +010042 idle = check_optional_name(rd.get('idle'), 'idle field of ' + what)
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010043 primary = check_bool(rd.get('primary', only_item),
44 'primary field of ' + what)
Timothy Chen1d0a8172021-09-28 13:40:51 -070045 internal = check_bool(rd.get('internal', False),
46 'internal field of ' + what)
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010047
Timothy Chena49ceb62021-07-13 14:59:09 -070048 match = re.match(r'^clk_([A-Za-z0-9_]+)_i', str(clock))
49 if not clock or clock in ['clk_i', 'scan_clk_i']:
50 clock_base_name = ""
51 elif match:
52 clock_base_name = match.group(1)
53 else:
54 raise ValueError(f'clock name must be of the form clk_*_i or clk_i. '
55 f'{clock} is illegal.')
56
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010057 if primary:
58 if clock is None:
59 raise ValueError('No clock signal for primary '
60 f'clocking item at {what}.')
61 if reset is None:
62 raise ValueError('No reset signal for primary '
63 f'clocking item at {what}.')
64
Timothy Chen1d0a8172021-09-28 13:40:51 -070065 return ClockingItem(clock, reset, idle, primary, internal, clock_base_name)
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010066
67 def _asdict(self) -> Dict[str, object]:
68 ret = {} # type: Dict[str, object]
69 if self.clock is not None:
70 ret['clock'] = self.clock,
71 if self.reset is not None:
72 ret['reset'] = self.reset
Rupert Swarbrick5aa249f2021-07-19 18:35:11 +010073 if self.idle is not None:
74 ret['idle'] = self.idle
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +010075
76 ret['primary'] = self.primary
77 return ret
78
79
80class Clocking:
81 def __init__(self, items: List[ClockingItem], primary: ClockingItem):
82 assert items
83 self.items = items
84 self.primary = primary
85
86 @staticmethod
87 def from_raw(raw: object, where: str) -> 'Clocking':
88 what = f'clocking items at {where}'
89 raw_items = check_list(raw, what)
90 if not raw_items:
91 raise ValueError(f'Empty list of clocking items at {where}.')
92
93 just_one_item = len(raw_items) == 1
94
95 items = []
96 primaries = []
97 for idx, raw_item in enumerate(raw_items):
98 item_where = f'entry {idx} of {what}'
99 item = ClockingItem.from_raw(raw_item, just_one_item, item_where)
100 if item.primary:
101 primaries.append(item)
102 items.append(item)
103
104 if len(primaries) != 1:
105 raise ValueError('There should be exactly one primary clocking '
106 f'item at {where}, but we saw {len(primaries)}.')
107
108 return Clocking(items, primaries[0])
109
110 def other_clocks(self) -> List[str]:
111 ret = []
112 for item in self.items:
113 if not item.primary and item.clock is not None:
114 ret.append(item.clock)
115 return ret
116
Timothy Chen1d0a8172021-09-28 13:40:51 -0700117 def clock_signals(self, ret_internal: bool = True) -> List[str]:
118 # By default clock_signals returns all clocks, including internal clocks.
119 # If the ret_internal input is set to false, then only externally supplied
120 # clocks are returned.
121 return [item.clock for item in self.items if item.clock is not None and
122 (ret_internal or not item.internal)]
Rupert Swarbrickd0cbfad2021-06-29 17:04:51 +0100123
124 def reset_signals(self) -> List[str]:
125 return [item.reset for item in self.items if item.reset is not None]
Timothy Chena49ceb62021-07-13 14:59:09 -0700126
127 def get_by_clock(self, name: Optional[str]) -> ClockingItem:
128 ret = None
129 for item in self.items:
130 if name == item.clock:
Timothy Chen1d0a8172021-09-28 13:40:51 -0700131 ret = item
132 break
Timothy Chena49ceb62021-07-13 14:59:09 -0700133
134 if ret is None:
135 raise ValueError(f'The requested clock {name} does not exist.')
136 else:
137 return ret