Rupert Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 1 | # 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 | |
| 7 | from typing import Dict, List, Optional |
Timothy Chen | a49ceb6 | 2021-07-13 14:59:09 -0700 | [diff] [blame] | 8 | import re |
Rupert Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 9 | |
Eunchan Kim | 2861a7c | 2022-07-29 11:52:36 -0700 | [diff] [blame] | 10 | from reggen.lib import check_keys, check_list, check_bool, check_optional_name |
| 11 | |
Rupert Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 12 | |
| 13 | class ClockingItem: |
Rupert Swarbrick | 5aa249f | 2021-07-19 18:35:11 +0100 | [diff] [blame] | 14 | def __init__(self, |
| 15 | clock: Optional[str], |
| 16 | reset: Optional[str], |
| 17 | idle: Optional[str], |
Timothy Chen | a49ceb6 | 2021-07-13 14:59:09 -0700 | [diff] [blame] | 18 | primary: bool, |
Timothy Chen | 1d0a817 | 2021-09-28 13:40:51 -0700 | [diff] [blame] | 19 | internal: bool, |
Timothy Chen | a49ceb6 | 2021-07-13 14:59:09 -0700 | [diff] [blame] | 20 | clock_base_name: Optional[str]): |
Rupert Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 21 | if primary: |
| 22 | assert clock is not None |
| 23 | assert reset is not None |
| 24 | |
| 25 | self.clock = clock |
Timothy Chen | a49ceb6 | 2021-07-13 14:59:09 -0700 | [diff] [blame] | 26 | self.clock_base_name = clock_base_name |
Rupert Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 27 | self.reset = reset |
| 28 | self.primary = primary |
Rupert Swarbrick | 5aa249f | 2021-07-19 18:35:11 +0100 | [diff] [blame] | 29 | self.idle = idle |
Timothy Chen | 1d0a817 | 2021-09-28 13:40:51 -0700 | [diff] [blame] | 30 | # 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 Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 34 | |
| 35 | @staticmethod |
| 36 | def from_raw(raw: object, only_item: bool, where: str) -> 'ClockingItem': |
| 37 | what = f'clocking item at {where}' |
Timothy Chen | 1d0a817 | 2021-09-28 13:40:51 -0700 | [diff] [blame] | 38 | rd = check_keys(raw, what, [], ['clock', 'reset', 'idle', 'primary', 'internal']) |
Rupert Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 39 | |
| 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 Swarbrick | 5aa249f | 2021-07-19 18:35:11 +0100 | [diff] [blame] | 42 | idle = check_optional_name(rd.get('idle'), 'idle field of ' + what) |
Rupert Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 43 | primary = check_bool(rd.get('primary', only_item), |
| 44 | 'primary field of ' + what) |
Timothy Chen | 1d0a817 | 2021-09-28 13:40:51 -0700 | [diff] [blame] | 45 | internal = check_bool(rd.get('internal', False), |
| 46 | 'internal field of ' + what) |
Rupert Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 47 | |
Timothy Chen | a49ceb6 | 2021-07-13 14:59:09 -0700 | [diff] [blame] | 48 | 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 Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 57 | 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 Chen | 1d0a817 | 2021-09-28 13:40:51 -0700 | [diff] [blame] | 65 | return ClockingItem(clock, reset, idle, primary, internal, clock_base_name) |
Rupert Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 66 | |
| 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 Swarbrick | 5aa249f | 2021-07-19 18:35:11 +0100 | [diff] [blame] | 73 | if self.idle is not None: |
| 74 | ret['idle'] = self.idle |
Rupert Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 75 | |
| 76 | ret['primary'] = self.primary |
| 77 | return ret |
| 78 | |
| 79 | |
| 80 | class 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 Chen | 1d0a817 | 2021-09-28 13:40:51 -0700 | [diff] [blame] | 117 | 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 Swarbrick | d0cbfad | 2021-06-29 17:04:51 +0100 | [diff] [blame] | 123 | |
| 124 | def reset_signals(self) -> List[str]: |
| 125 | return [item.reset for item in self.items if item.reset is not None] |
Timothy Chen | a49ceb6 | 2021-07-13 14:59:09 -0700 | [diff] [blame] | 126 | |
| 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 Chen | 1d0a817 | 2021-09-28 13:40:51 -0700 | [diff] [blame] | 131 | ret = item |
| 132 | break |
Timothy Chen | a49ceb6 | 2021-07-13 14:59:09 -0700 | [diff] [blame] | 133 | |
| 134 | if ret is None: |
| 135 | raise ValueError(f'The requested clock {name} does not exist.') |
| 136 | else: |
| 137 | return ret |