Sam Elliott | e74c629 | 2020-07-24 21:40:00 +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 | """This contains a class which is used to help generate `top_{name}.h` and |
| 5 | `top_{name}.h`. |
| 6 | """ |
Srikrishna Iyer | 689981e | 2021-09-16 12:12:37 -0700 | [diff] [blame] | 7 | from collections import OrderedDict, defaultdict |
Rupert Swarbrick | 200d8b4 | 2021-03-08 12:32:11 +0000 | [diff] [blame] | 8 | from typing import Dict, List, Optional, Tuple |
Sam Elliott | 7931f2f | 2020-07-24 23:21:41 +0100 | [diff] [blame] | 9 | |
Sam Elliott | 66efff0 | 2020-07-24 22:35:10 +0100 | [diff] [blame] | 10 | from mako.template import Template |
Rupert Swarbrick | 200d8b4 | 2021-03-08 12:32:11 +0000 | [diff] [blame] | 11 | from reggen.ip_block import IpBlock |
| 12 | |
Srikrishna Iyer | 689981e | 2021-09-16 12:12:37 -0700 | [diff] [blame] | 13 | from .lib import Name, get_base_and_size |
| 14 | |
| 15 | C_FILE_EXTENSIONS = (".c", ".h", ".cc", ".inc") |
| 16 | |
Sam Elliott | e74c629 | 2020-07-24 21:40:00 +0100 | [diff] [blame] | 17 | |
Sam Elliott | e74c629 | 2020-07-24 21:40:00 +0100 | [diff] [blame] | 18 | class MemoryRegion(object): |
Rupert Swarbrick | 200d8b4 | 2021-03-08 12:32:11 +0000 | [diff] [blame] | 19 | def __init__(self, name: Name, base_addr: int, size_bytes: int): |
| 20 | assert isinstance(base_addr, int) |
Sam Elliott | e74c629 | 2020-07-24 21:40:00 +0100 | [diff] [blame] | 21 | self.name = name |
| 22 | self.base_addr = base_addr |
| 23 | self.size_bytes = size_bytes |
Rupert Swarbrick | 200d8b4 | 2021-03-08 12:32:11 +0000 | [diff] [blame] | 24 | self.size_words = (size_bytes + 3) // 4 |
Sam Elliott | e74c629 | 2020-07-24 21:40:00 +0100 | [diff] [blame] | 25 | |
| 26 | def base_addr_name(self): |
| 27 | return self.name + Name(["base", "addr"]) |
| 28 | |
Silvestrs Timofejevs | a2d4a6a | 2020-10-08 10:47:54 +0100 | [diff] [blame] | 29 | def offset_name(self): |
| 30 | return self.name + Name(["offset"]) |
| 31 | |
Sam Elliott | e74c629 | 2020-07-24 21:40:00 +0100 | [diff] [blame] | 32 | def size_bytes_name(self): |
| 33 | return self.name + Name(["size", "bytes"]) |
| 34 | |
Silvestrs Timofejevs | a2d4a6a | 2020-10-08 10:47:54 +0100 | [diff] [blame] | 35 | def size_words_name(self): |
| 36 | return self.name + Name(["size", "words"]) |
| 37 | |
Sam Elliott | e74c629 | 2020-07-24 21:40:00 +0100 | [diff] [blame] | 38 | |
Sam Elliott | 66efff0 | 2020-07-24 22:35:10 +0100 | [diff] [blame] | 39 | class CEnum(object): |
| 40 | def __init__(self, name): |
| 41 | self.name = name |
| 42 | self.enum_counter = 0 |
| 43 | self.finalized = False |
| 44 | |
| 45 | self.constants = [] |
| 46 | |
| 47 | def add_constant(self, constant_name, docstring=""): |
| 48 | assert not self.finalized |
| 49 | |
| 50 | full_name = self.name + constant_name |
| 51 | |
| 52 | value = self.enum_counter |
| 53 | self.enum_counter += 1 |
| 54 | |
| 55 | self.constants.append((full_name, value, docstring)) |
| 56 | |
Sam Elliott | 7931f2f | 2020-07-24 23:21:41 +0100 | [diff] [blame] | 57 | return full_name |
| 58 | |
Sam Elliott | 66efff0 | 2020-07-24 22:35:10 +0100 | [diff] [blame] | 59 | def add_last_constant(self, docstring=""): |
| 60 | assert not self.finalized |
| 61 | |
| 62 | full_name = self.name + Name(["last"]) |
| 63 | |
| 64 | _, last_val, _ = self.constants[-1] |
| 65 | |
| 66 | self.constants.append((full_name, last_val, r"\internal " + docstring)) |
| 67 | self.finalized = True |
| 68 | |
| 69 | def render(self): |
| 70 | template = ("typedef enum ${enum.name.as_snake_case()} {\n" |
| 71 | "% for name, value, docstring in enum.constants:\n" |
| 72 | " ${name.as_c_enum()} = ${value}, /**< ${docstring} */\n" |
| 73 | "% endfor\n" |
| 74 | "} ${enum.name.as_c_type()};") |
| 75 | return Template(template).render(enum=self) |
| 76 | |
| 77 | |
Sam Elliott | 7931f2f | 2020-07-24 23:21:41 +0100 | [diff] [blame] | 78 | class CArrayMapping(object): |
| 79 | def __init__(self, name, output_type_name): |
| 80 | self.name = name |
| 81 | self.output_type_name = output_type_name |
| 82 | |
| 83 | self.mapping = OrderedDict() |
| 84 | |
| 85 | def add_entry(self, in_name, out_name): |
| 86 | self.mapping[in_name] = out_name |
| 87 | |
| 88 | def render_declaration(self): |
| 89 | template = ( |
| 90 | "extern const ${mapping.output_type_name.as_c_type()}\n" |
| 91 | " ${mapping.name.as_snake_case()}[${len(mapping.mapping)}];") |
| 92 | return Template(template).render(mapping=self) |
| 93 | |
| 94 | def render_definition(self): |
| 95 | template = ( |
| 96 | "const ${mapping.output_type_name.as_c_type()}\n" |
| 97 | " ${mapping.name.as_snake_case()}[${len(mapping.mapping)}] = {\n" |
| 98 | "% for in_name, out_name in mapping.mapping.items():\n" |
| 99 | " [${in_name.as_c_enum()}] = ${out_name.as_c_enum()},\n" |
| 100 | "% endfor\n" |
| 101 | "};\n") |
| 102 | return Template(template).render(mapping=self) |
| 103 | |
| 104 | |
Rupert Swarbrick | 200d8b4 | 2021-03-08 12:32:11 +0000 | [diff] [blame] | 105 | class TopGenC: |
| 106 | def __init__(self, top_info, name_to_block: Dict[str, IpBlock]): |
Sam Elliott | e74c629 | 2020-07-24 21:40:00 +0100 | [diff] [blame] | 107 | self.top = top_info |
Sam Elliott | 66efff0 | 2020-07-24 22:35:10 +0100 | [diff] [blame] | 108 | self._top_name = Name(["top"]) + Name.from_snake_case(top_info["name"]) |
Rupert Swarbrick | 200d8b4 | 2021-03-08 12:32:11 +0000 | [diff] [blame] | 109 | self._name_to_block = name_to_block |
Sam Elliott | e74c629 | 2020-07-24 21:40:00 +0100 | [diff] [blame] | 110 | |
Sam Elliott | d179047 | 2020-07-24 23:25:10 +0100 | [diff] [blame] | 111 | # The .c file needs the .h file's relative path, store it here |
| 112 | self.header_path = None |
| 113 | |
Sam Elliott | 7931f2f | 2020-07-24 23:21:41 +0100 | [diff] [blame] | 114 | self._init_plic_targets() |
| 115 | self._init_plic_mapping() |
Sam Elliott | a7b7b5f | 2020-07-24 23:46:36 +0100 | [diff] [blame] | 116 | self._init_alert_mapping() |
Sam Elliott | fbb34ec | 2020-07-25 02:00:22 +0100 | [diff] [blame] | 117 | self._init_pinmux_mapping() |
Timothy Chen | 2fb2767 | 2022-09-09 12:20:00 -0700 | [diff] [blame] | 118 | self._init_pad_mapping() |
Sam Elliott | 1625b63 | 2020-08-17 15:08:43 +0100 | [diff] [blame] | 119 | self._init_pwrmgr_wakeups() |
Timothy Chen | d9d088b | 2020-09-25 13:14:09 -0700 | [diff] [blame] | 120 | self._init_rstmgr_sw_rsts() |
Timothy Chen | f8726cd | 2020-09-24 14:05:37 -0700 | [diff] [blame] | 121 | self._init_pwrmgr_reset_requests() |
Sam Elliott | 9d7a1e3 | 2020-10-19 18:45:50 +0100 | [diff] [blame] | 122 | self._init_clkmgr_clocks() |
Alphan Ulusoy | 3c16994 | 2022-08-17 11:47:28 -0400 | [diff] [blame] | 123 | self._init_mmio_region() |
Sam Elliott | a7b7b5f | 2020-07-24 23:46:36 +0100 | [diff] [blame] | 124 | |
Rupert Swarbrick | 200d8b4 | 2021-03-08 12:32:11 +0000 | [diff] [blame] | 125 | def devices(self) -> List[Tuple[Tuple[str, Optional[str]], MemoryRegion]]: |
| 126 | '''Return a list of MemoryRegion objects for devices on the bus |
| 127 | |
| 128 | The list returned is pairs (full_if, region) where full_if is itself a |
| 129 | pair (inst_name, if_name). inst_name is the name of some IP block |
| 130 | instantiation. if_name is the name of the interface (may be None). |
| 131 | region is a MemoryRegion object representing the device. |
| 132 | |
| 133 | ''' |
| 134 | ret = [] # type: List[Tuple[Tuple[str, Optional[str]], MemoryRegion]] |
Srikrishna Iyer | 689981e | 2021-09-16 12:12:37 -0700 | [diff] [blame] | 135 | # TODO: This method is invoked in templates, as well as in the extended |
| 136 | # class TopGenCTest. We could refactor and optimize the implementation |
| 137 | # a bit. |
| 138 | self.device_regions = defaultdict(dict) |
Rupert Swarbrick | 200d8b4 | 2021-03-08 12:32:11 +0000 | [diff] [blame] | 139 | for inst in self.top['module']: |
| 140 | block = self._name_to_block[inst['type']] |
| 141 | for if_name, rb in block.reg_blocks.items(): |
| 142 | full_if = (inst['name'], if_name) |
| 143 | full_if_name = Name.from_snake_case(full_if[0]) |
| 144 | if if_name is not None: |
| 145 | full_if_name += Name.from_snake_case(if_name) |
| 146 | |
| 147 | name = self._top_name + full_if_name |
| 148 | base, size = get_base_and_size(self._name_to_block, |
| 149 | inst, if_name) |
| 150 | |
| 151 | region = MemoryRegion(name, base, size) |
Srikrishna Iyer | 689981e | 2021-09-16 12:12:37 -0700 | [diff] [blame] | 152 | self.device_regions[inst['name']].update({if_name: region}) |
Rupert Swarbrick | 200d8b4 | 2021-03-08 12:32:11 +0000 | [diff] [blame] | 153 | ret.append((full_if, region)) |
| 154 | |
| 155 | return ret |
Sam Elliott | a7b7b5f | 2020-07-24 23:46:36 +0100 | [diff] [blame] | 156 | |
| 157 | def memories(self): |
Michael Schaffner | 02e982f | 2021-07-09 17:40:34 -0700 | [diff] [blame] | 158 | ret = [] |
| 159 | for m in self.top["memory"]: |
| 160 | ret.append((m["name"], |
| 161 | MemoryRegion(self._top_name + |
| 162 | Name.from_snake_case(m["name"]), |
| 163 | int(m["base_addr"], 0), |
| 164 | int(m["size"], 0)))) |
| 165 | |
| 166 | for inst in self.top['module']: |
| 167 | if "memory" in inst: |
| 168 | for if_name, val in inst["memory"].items(): |
| 169 | base, size = get_base_and_size(self._name_to_block, |
| 170 | inst, if_name) |
| 171 | |
| 172 | name = self._top_name + Name.from_snake_case(val["label"]) |
| 173 | region = MemoryRegion(name, base, size) |
| 174 | ret.append((val["label"], region)) |
| 175 | |
| 176 | return ret |
Sam Elliott | 7931f2f | 2020-07-24 23:21:41 +0100 | [diff] [blame] | 177 | |
| 178 | def _init_plic_targets(self): |
| 179 | enum = CEnum(self._top_name + Name(["plic", "target"])) |
| 180 | |
| 181 | for core_id in range(int(self.top["num_cores"])): |
| 182 | enum.add_constant(Name(["ibex", str(core_id)]), |
| 183 | docstring="Ibex Core {}".format(core_id)) |
| 184 | |
| 185 | enum.add_last_constant("Final PLIC target") |
| 186 | |
| 187 | self.plic_targets = enum |
| 188 | |
| 189 | def _init_plic_mapping(self): |
| 190 | """We eventually want to generate a mapping from interrupt id to the |
| 191 | source peripheral. |
| 192 | |
Sam Elliott | a7b7b5f | 2020-07-24 23:46:36 +0100 | [diff] [blame] | 193 | In order to do so, we generate two enums (one for interrupts, one for |
| 194 | sources), and store the generated names in a dictionary that represents |
| 195 | the mapping. |
Sam Elliott | 7931f2f | 2020-07-24 23:21:41 +0100 | [diff] [blame] | 196 | |
Sam Elliott | a7b7b5f | 2020-07-24 23:46:36 +0100 | [diff] [blame] | 197 | PLIC Interrupt ID 0 corresponds to no interrupt, and so no peripheral, |
Sam Elliott | 7931f2f | 2020-07-24 23:21:41 +0100 | [diff] [blame] | 198 | so we encode that in the enum as "unknown". |
| 199 | |
| 200 | The interrupts have to be added in order, with "none" first, to ensure |
| 201 | that they get the correct mapping to their PLIC id, which is used for |
| 202 | addressing the right registers and bits. |
| 203 | """ |
| 204 | sources = CEnum(self._top_name + Name(["plic", "peripheral"])) |
| 205 | interrupts = CEnum(self._top_name + Name(["plic", "irq", "id"])) |
| 206 | plic_mapping = CArrayMapping( |
| 207 | self._top_name + Name(["plic", "interrupt", "for", "peripheral"]), |
| 208 | sources.name) |
| 209 | |
| 210 | unknown_source = sources.add_constant(Name(["unknown"]), |
| 211 | docstring="Unknown Peripheral") |
| 212 | none_irq_id = interrupts.add_constant(Name(["none"]), |
| 213 | docstring="No Interrupt") |
| 214 | plic_mapping.add_entry(none_irq_id, unknown_source) |
| 215 | |
| 216 | # When we generate the `interrupts` enum, the only info we have about |
| 217 | # the source is the module name. We'll use `source_name_map` to map a |
| 218 | # short module name to the full name object used for the enum constant. |
| 219 | source_name_map = {} |
| 220 | |
| 221 | for name in self.top["interrupt_module"]: |
| 222 | source_name = sources.add_constant(Name.from_snake_case(name), |
| 223 | docstring=name) |
| 224 | source_name_map[name] = source_name |
| 225 | |
| 226 | sources.add_last_constant("Final PLIC peripheral") |
| 227 | |
Srikrishna Iyer | 689981e | 2021-09-16 12:12:37 -0700 | [diff] [blame] | 228 | # Maintain a list of instance-specific IRQs by instance name. |
| 229 | self.device_irqs = defaultdict(list) |
Sam Elliott | 7931f2f | 2020-07-24 23:21:41 +0100 | [diff] [blame] | 230 | for intr in self.top["interrupt"]: |
| 231 | # Some interrupts are multiple bits wide. Here we deal with that by |
| 232 | # adding a bit-index suffix |
| 233 | if "width" in intr and int(intr["width"]) != 1: |
| 234 | for i in range(int(intr["width"])): |
Weicai Yang | 53b0d4d | 2020-11-30 15:28:33 -0800 | [diff] [blame] | 235 | name = Name.from_snake_case(intr["name"]) + Name([str(i)]) |
Sam Elliott | 7931f2f | 2020-07-24 23:21:41 +0100 | [diff] [blame] | 236 | irq_id = interrupts.add_constant(name, |
| 237 | docstring="{} {}".format( |
| 238 | intr["name"], i)) |
| 239 | source_name = source_name_map[intr["module_name"]] |
| 240 | plic_mapping.add_entry(irq_id, source_name) |
Srikrishna Iyer | 689981e | 2021-09-16 12:12:37 -0700 | [diff] [blame] | 241 | self.device_irqs[intr["module_name"]].append(intr["name"] + |
| 242 | str(i)) |
Sam Elliott | 7931f2f | 2020-07-24 23:21:41 +0100 | [diff] [blame] | 243 | else: |
| 244 | name = Name.from_snake_case(intr["name"]) |
| 245 | irq_id = interrupts.add_constant(name, docstring=intr["name"]) |
| 246 | source_name = source_name_map[intr["module_name"]] |
| 247 | plic_mapping.add_entry(irq_id, source_name) |
Srikrishna Iyer | 689981e | 2021-09-16 12:12:37 -0700 | [diff] [blame] | 248 | self.device_irqs[intr["module_name"]].append(intr["name"]) |
Sam Elliott | 7931f2f | 2020-07-24 23:21:41 +0100 | [diff] [blame] | 249 | |
| 250 | interrupts.add_last_constant("The Last Valid Interrupt ID.") |
| 251 | |
| 252 | self.plic_sources = sources |
| 253 | self.plic_interrupts = interrupts |
| 254 | self.plic_mapping = plic_mapping |
| 255 | |
Sam Elliott | a7b7b5f | 2020-07-24 23:46:36 +0100 | [diff] [blame] | 256 | def _init_alert_mapping(self): |
| 257 | """We eventually want to generate a mapping from alert id to the source |
| 258 | peripheral. |
Sam Elliott | e74c629 | 2020-07-24 21:40:00 +0100 | [diff] [blame] | 259 | |
Sam Elliott | a7b7b5f | 2020-07-24 23:46:36 +0100 | [diff] [blame] | 260 | In order to do so, we generate two enums (one for alerts, one for |
| 261 | sources), and store the generated names in a dictionary that represents |
| 262 | the mapping. |
| 263 | |
| 264 | Alert Handler has no concept of "no alert", unlike the PLIC. |
| 265 | |
| 266 | The alerts have to be added in order, to ensure that they get the |
| 267 | correct mapping to their alert id, which is used for addressing the |
| 268 | right registers and bits. |
| 269 | """ |
| 270 | sources = CEnum(self._top_name + Name(["alert", "peripheral"])) |
| 271 | alerts = CEnum(self._top_name + Name(["alert", "id"])) |
| 272 | alert_mapping = CArrayMapping( |
| 273 | self._top_name + Name(["alert", "for", "peripheral"]), |
| 274 | sources.name) |
| 275 | |
| 276 | # When we generate the `alerts` enum, the only info we have about the |
| 277 | # source is the module name. We'll use `source_name_map` to map a short |
| 278 | # module name to the full name object used for the enum constant. |
| 279 | source_name_map = {} |
| 280 | |
| 281 | for name in self.top["alert_module"]: |
| 282 | source_name = sources.add_constant(Name.from_snake_case(name), |
| 283 | docstring=name) |
| 284 | source_name_map[name] = source_name |
| 285 | |
| 286 | sources.add_last_constant("Final Alert peripheral") |
| 287 | |
Cindy Chen | 21447a2 | 2022-04-18 17:55:25 -0700 | [diff] [blame] | 288 | self.device_alerts = defaultdict(list) |
Sam Elliott | a7b7b5f | 2020-07-24 23:46:36 +0100 | [diff] [blame] | 289 | for alert in self.top["alert"]: |
| 290 | if "width" in alert and int(alert["width"]) != 1: |
| 291 | for i in range(int(alert["width"])): |
Weicai Yang | 53b0d4d | 2020-11-30 15:28:33 -0800 | [diff] [blame] | 292 | name = Name.from_snake_case(alert["name"]) + Name([str(i)]) |
| 293 | irq_id = alerts.add_constant(name, |
| 294 | docstring="{} {}".format( |
| 295 | alert["name"], i)) |
Sam Elliott | a7b7b5f | 2020-07-24 23:46:36 +0100 | [diff] [blame] | 296 | source_name = source_name_map[alert["module_name"]] |
| 297 | alert_mapping.add_entry(irq_id, source_name) |
Cindy Chen | 21447a2 | 2022-04-18 17:55:25 -0700 | [diff] [blame] | 298 | self.device_alerts[alert["module_name"]].append(alert["name"] + |
| 299 | str(i)) |
Sam Elliott | a7b7b5f | 2020-07-24 23:46:36 +0100 | [diff] [blame] | 300 | else: |
| 301 | name = Name.from_snake_case(alert["name"]) |
| 302 | alert_id = alerts.add_constant(name, docstring=alert["name"]) |
| 303 | source_name = source_name_map[alert["module_name"]] |
| 304 | alert_mapping.add_entry(alert_id, source_name) |
Cindy Chen | 21447a2 | 2022-04-18 17:55:25 -0700 | [diff] [blame] | 305 | self.device_alerts[alert["module_name"]].append(alert["name"]) |
Sam Elliott | a7b7b5f | 2020-07-24 23:46:36 +0100 | [diff] [blame] | 306 | |
| 307 | alerts.add_last_constant("The Last Valid Alert ID.") |
| 308 | |
| 309 | self.alert_sources = sources |
| 310 | self.alert_alerts = alerts |
| 311 | self.alert_mapping = alert_mapping |
Sam Elliott | fbb34ec | 2020-07-25 02:00:22 +0100 | [diff] [blame] | 312 | |
| 313 | def _init_pinmux_mapping(self): |
| 314 | """Generate C enums for addressing pinmux registers and in/out selects. |
| 315 | |
Michael Schaffner | b5b8eba | 2021-02-09 20:07:04 -0800 | [diff] [blame] | 316 | Inputs/outputs are connected in the order the modules are listed in |
| 317 | the hjson under the "mio_modules" key. For each module, the corresponding |
| 318 | inouts are connected first, followed by either the inputs or the outputs. |
Sam Elliott | fbb34ec | 2020-07-25 02:00:22 +0100 | [diff] [blame] | 319 | |
| 320 | Inputs: |
| 321 | - Peripheral chooses register field (pinmux_peripheral_in) |
| 322 | - Insel chooses MIO input (pinmux_insel) |
| 323 | |
| 324 | Outputs: |
| 325 | - MIO chooses register field (pinmux_mio_out) |
| 326 | - Outsel chooses peripheral output (pinmux_outsel) |
| 327 | |
| 328 | Insel and outsel have some special values which are captured here too. |
| 329 | """ |
Michael Schaffner | 74c4ff2 | 2021-03-30 15:43:46 -0700 | [diff] [blame] | 330 | pinmux_info = self.top['pinmux'] |
| 331 | pinout_info = self.top['pinout'] |
Sam Elliott | fbb34ec | 2020-07-25 02:00:22 +0100 | [diff] [blame] | 332 | |
| 333 | # Peripheral Inputs |
| 334 | peripheral_in = CEnum(self._top_name + |
Michael Schaffner | 74c4ff2 | 2021-03-30 15:43:46 -0700 | [diff] [blame] | 335 | Name(['pinmux', 'peripheral', 'in'])) |
| 336 | i = 0 |
| 337 | for sig in pinmux_info['ios']: |
| 338 | if sig['connection'] == 'muxed' and sig['type'] in ['inout', 'input']: |
| 339 | index = Name([str(sig['idx'])]) if sig['idx'] != -1 else Name([]) |
| 340 | name = Name.from_snake_case(sig['name']) + index |
| 341 | peripheral_in.add_constant(name, docstring='Peripheral Input {}'.format(i)) |
| 342 | i += 1 |
| 343 | |
| 344 | peripheral_in.add_last_constant('Last valid peripheral input') |
Sam Elliott | fbb34ec | 2020-07-25 02:00:22 +0100 | [diff] [blame] | 345 | |
| 346 | # Pinmux Input Selects |
Michael Schaffner | 74c4ff2 | 2021-03-30 15:43:46 -0700 | [diff] [blame] | 347 | insel = CEnum(self._top_name + Name(['pinmux', 'insel'])) |
| 348 | insel.add_constant(Name(['constant', 'zero']), |
| 349 | docstring='Tie constantly to zero') |
| 350 | insel.add_constant(Name(['constant', 'one']), |
| 351 | docstring='Tie constantly to one') |
| 352 | i = 0 |
| 353 | for pad in pinout_info['pads']: |
| 354 | if pad['connection'] == 'muxed': |
| 355 | insel.add_constant(Name([pad['name']]), |
| 356 | docstring='MIO Pad {}'.format(i)) |
| 357 | i += 1 |
| 358 | insel.add_last_constant('Last valid insel value') |
Sam Elliott | fbb34ec | 2020-07-25 02:00:22 +0100 | [diff] [blame] | 359 | |
| 360 | # MIO Outputs |
Michael Schaffner | 74c4ff2 | 2021-03-30 15:43:46 -0700 | [diff] [blame] | 361 | mio_out = CEnum(self._top_name + Name(['pinmux', 'mio', 'out'])) |
| 362 | i = 0 |
| 363 | for pad in pinout_info['pads']: |
| 364 | if pad['connection'] == 'muxed': |
| 365 | mio_out.add_constant(Name.from_snake_case(pad['name']), |
| 366 | docstring='MIO Pad {}'.format(i)) |
| 367 | i += 1 |
| 368 | mio_out.add_last_constant('Last valid mio output') |
Sam Elliott | fbb34ec | 2020-07-25 02:00:22 +0100 | [diff] [blame] | 369 | |
| 370 | # Pinmux Output Selects |
Michael Schaffner | 74c4ff2 | 2021-03-30 15:43:46 -0700 | [diff] [blame] | 371 | outsel = CEnum(self._top_name + Name(['pinmux', 'outsel'])) |
| 372 | outsel.add_constant(Name(['constant', 'zero']), |
| 373 | docstring='Tie constantly to zero') |
| 374 | outsel.add_constant(Name(['constant', 'one']), |
| 375 | docstring='Tie constantly to one') |
| 376 | outsel.add_constant(Name(['constant', 'high', 'z']), |
| 377 | docstring='Tie constantly to high-Z') |
| 378 | i = 0 |
| 379 | for sig in pinmux_info['ios']: |
| 380 | if sig['connection'] == 'muxed' and sig['type'] in ['inout', 'output']: |
| 381 | index = Name([str(sig['idx'])]) if sig['idx'] != -1 else Name([]) |
| 382 | name = Name.from_snake_case(sig['name']) + index |
| 383 | outsel.add_constant(name, docstring='Peripheral Output {}'.format(i)) |
| 384 | i += 1 |
| 385 | |
| 386 | outsel.add_last_constant('Last valid outsel value') |
Sam Elliott | fbb34ec | 2020-07-25 02:00:22 +0100 | [diff] [blame] | 387 | |
| 388 | self.pinmux_peripheral_in = peripheral_in |
| 389 | self.pinmux_insel = insel |
| 390 | self.pinmux_mio_out = mio_out |
| 391 | self.pinmux_outsel = outsel |
Sam Elliott | 1625b63 | 2020-08-17 15:08:43 +0100 | [diff] [blame] | 392 | |
Timothy Chen | 2fb2767 | 2022-09-09 12:20:00 -0700 | [diff] [blame] | 393 | def _init_pad_mapping(self): |
| 394 | """Generate C enums for order of MIO and DIO pads. |
| 395 | |
| 396 | These are needed to configure pad specific configurations such as |
| 397 | slew rate and other flags. |
| 398 | """ |
| 399 | direct_enum = CEnum(self._top_name + |
| 400 | Name(["direct", "pads"])) |
| 401 | |
| 402 | muxed_enum = CEnum(self._top_name + |
| 403 | Name(["muxed", "pads"])) |
| 404 | |
| 405 | pads_info = self.top['pinout']['pads'] |
| 406 | muxed = [pad['name'] for pad in pads_info if pad['connection'] == 'muxed'] |
| 407 | |
| 408 | # The logic here follows the sequence done in toplevel_pkg.sv.tpl. |
| 409 | # The direct pads do not enumerate directly from the pinout like the muxed |
| 410 | # ios. Instead it follows a direction from the pinmux perspective. |
| 411 | pads_info = self.top['pinmux']['ios'] |
| 412 | direct = [pad for pad in pads_info if pad['connection'] != 'muxed'] |
| 413 | |
| 414 | for pad in (direct): |
| 415 | name = f"{pad['name']}" |
| 416 | if pad['width'] > 1: |
| 417 | name = f"{name}{pad['idx']}" |
| 418 | |
| 419 | direct_enum.add_constant( |
| 420 | Name.from_snake_case(name)) |
| 421 | direct_enum.add_last_constant("Last valid direct pad") |
| 422 | |
| 423 | for pad in (muxed): |
| 424 | muxed_enum.add_constant( |
| 425 | Name.from_snake_case(pad)) |
| 426 | muxed_enum.add_last_constant("Last valid muxed pad") |
| 427 | |
| 428 | self.direct_pads = direct_enum |
| 429 | self.muxed_pads = muxed_enum |
| 430 | |
| 431 | |
Sam Elliott | 1625b63 | 2020-08-17 15:08:43 +0100 | [diff] [blame] | 432 | def _init_pwrmgr_wakeups(self): |
Weicai Yang | 53b0d4d | 2020-11-30 15:28:33 -0800 | [diff] [blame] | 433 | enum = CEnum(self._top_name + |
| 434 | Name(["power", "manager", "wake", "ups"])) |
Sam Elliott | 1625b63 | 2020-08-17 15:08:43 +0100 | [diff] [blame] | 435 | |
| 436 | for signal in self.top["wakeups"]: |
Weicai Yang | 53b0d4d | 2020-11-30 15:28:33 -0800 | [diff] [blame] | 437 | enum.add_constant( |
| 438 | Name.from_snake_case(signal["module"]) + |
| 439 | Name.from_snake_case(signal["name"])) |
Sam Elliott | 1625b63 | 2020-08-17 15:08:43 +0100 | [diff] [blame] | 440 | |
| 441 | enum.add_last_constant("Last valid pwrmgr wakeup signal") |
| 442 | |
| 443 | self.pwrmgr_wakeups = enum |
Timothy Chen | d9d088b | 2020-09-25 13:14:09 -0700 | [diff] [blame] | 444 | |
| 445 | # Enumerates the positions of all software controllable resets |
| 446 | def _init_rstmgr_sw_rsts(self): |
Timothy Chen | 7c3de6e | 2021-07-19 16:46:45 -0700 | [diff] [blame] | 447 | sw_rsts = self.top['resets'].get_sw_resets() |
Timothy Chen | d9d088b | 2020-09-25 13:14:09 -0700 | [diff] [blame] | 448 | |
Weicai Yang | 53b0d4d | 2020-11-30 15:28:33 -0800 | [diff] [blame] | 449 | enum = CEnum(self._top_name + |
| 450 | Name(["reset", "manager", "sw", "resets"])) |
Timothy Chen | d9d088b | 2020-09-25 13:14:09 -0700 | [diff] [blame] | 451 | |
| 452 | for rst in sw_rsts: |
Timothy Chen | 7c3de6e | 2021-07-19 16:46:45 -0700 | [diff] [blame] | 453 | enum.add_constant(Name.from_snake_case(rst)) |
Timothy Chen | d9d088b | 2020-09-25 13:14:09 -0700 | [diff] [blame] | 454 | |
| 455 | enum.add_last_constant("Last valid rstmgr software reset request") |
| 456 | |
| 457 | self.rstmgr_sw_rsts = enum |
Timothy Chen | f8726cd | 2020-09-24 14:05:37 -0700 | [diff] [blame] | 458 | |
| 459 | def _init_pwrmgr_reset_requests(self): |
Weicai Yang | 53b0d4d | 2020-11-30 15:28:33 -0800 | [diff] [blame] | 460 | enum = CEnum(self._top_name + |
| 461 | Name(["power", "manager", "reset", "requests"])) |
Timothy Chen | f8726cd | 2020-09-24 14:05:37 -0700 | [diff] [blame] | 462 | |
| 463 | for signal in self.top["reset_requests"]: |
Weicai Yang | 53b0d4d | 2020-11-30 15:28:33 -0800 | [diff] [blame] | 464 | enum.add_constant( |
| 465 | Name.from_snake_case(signal["module"]) + |
| 466 | Name.from_snake_case(signal["name"])) |
Timothy Chen | f8726cd | 2020-09-24 14:05:37 -0700 | [diff] [blame] | 467 | |
| 468 | enum.add_last_constant("Last valid pwrmgr reset_request signal") |
| 469 | |
| 470 | self.pwrmgr_reset_requests = enum |
Sam Elliott | 9d7a1e3 | 2020-10-19 18:45:50 +0100 | [diff] [blame] | 471 | |
| 472 | def _init_clkmgr_clocks(self): |
| 473 | """ |
| 474 | Creates CEnums for accessing the software-controlled clocks in the |
| 475 | design. |
| 476 | |
| 477 | The logic here matches the logic in topgen.py in how it instantiates the |
| 478 | clock manager with the described clocks. |
| 479 | |
| 480 | We differentiate "gateable" clocks and "hintable" clocks because the |
| 481 | clock manager has separate register interfaces for each group. |
| 482 | """ |
Rupert Swarbrick | 127b109 | 2021-07-16 17:10:39 +0100 | [diff] [blame] | 483 | clocks = self.top['clocks'] |
Sam Elliott | 9d7a1e3 | 2020-10-19 18:45:50 +0100 | [diff] [blame] | 484 | |
Sam Elliott | 9d7a1e3 | 2020-10-19 18:45:50 +0100 | [diff] [blame] | 485 | gateable_clocks = CEnum(self._top_name + Name(["gateable", "clocks"])) |
| 486 | hintable_clocks = CEnum(self._top_name + Name(["hintable", "clocks"])) |
| 487 | |
Rupert Swarbrick | 0c9cf5c | 2021-07-19 11:55:18 +0100 | [diff] [blame] | 488 | c2g = clocks.make_clock_to_group() |
| 489 | by_type = clocks.typed_clocks() |
Sam Elliott | 9d7a1e3 | 2020-10-19 18:45:50 +0100 | [diff] [blame] | 490 | |
Rupert Swarbrick | 0c9cf5c | 2021-07-19 11:55:18 +0100 | [diff] [blame] | 491 | for name in by_type.sw_clks.keys(): |
| 492 | # All these clocks start with `clk_` which is redundant. |
| 493 | clock_name = Name.from_snake_case(name).remove_part("clk") |
| 494 | docstring = "Clock {} in group {}".format(name, c2g[name].name) |
| 495 | gateable_clocks.add_constant(clock_name, docstring) |
Sam Elliott | 9d7a1e3 | 2020-10-19 18:45:50 +0100 | [diff] [blame] | 496 | gateable_clocks.add_last_constant("Last Valid Gateable Clock") |
Rupert Swarbrick | 0c9cf5c | 2021-07-19 11:55:18 +0100 | [diff] [blame] | 497 | |
| 498 | for name in by_type.hint_clks.keys(): |
| 499 | # All these clocks start with `clk_` which is redundant. |
| 500 | clock_name = Name.from_snake_case(name).remove_part("clk") |
| 501 | docstring = "Clock {} in group {}".format(name, c2g[name].name) |
| 502 | hintable_clocks.add_constant(clock_name, docstring) |
Sam Elliott | 9d7a1e3 | 2020-10-19 18:45:50 +0100 | [diff] [blame] | 503 | hintable_clocks.add_last_constant("Last Valid Hintable Clock") |
| 504 | |
| 505 | self.clkmgr_gateable_clocks = gateable_clocks |
| 506 | self.clkmgr_hintable_clocks = hintable_clocks |
Alphan Ulusoy | 3c16994 | 2022-08-17 11:47:28 -0400 | [diff] [blame] | 507 | |
| 508 | def _init_mmio_region(self): |
| 509 | """ |
| 510 | Computes the bounds of the MMIO region. |
| 511 | |
| 512 | MMIO region excludes any memory that is separate from the module configuration |
| 513 | space, i.e. ROM, main SRAM, and flash are excluded but retention SRAM, |
| 514 | spi_device memory, or usbdev memory are included. |
| 515 | """ |
| 516 | memories = [region.base_addr for (_, region) in self.memories()] |
| 517 | # TODO(#14345): Remove the hardcoded "rv_dm" name check below. |
| 518 | regions = [ |
| 519 | region for ((dev_name, _), region) in self.devices() |
| 520 | if region.base_addr not in memories and dev_name != "rv_dm" |
| 521 | ] |
| 522 | # Note: The memory interface of the retention RAM is in the MMIO address space, |
| 523 | # which we prefer since it reduces the number of ePMP regions we need. |
| 524 | mmio = range(min([r.base_addr for r in regions]), |
| 525 | max([r.base_addr + r.size_bytes for r in regions])) |
| 526 | self.mmio = MemoryRegion(self._top_name + Name(["mmio"]), mmio.start, |
| 527 | mmio.stop - mmio.start) |