| # Copyright 2023 Google LLC | |
| # Copyright lowRISC contributors | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| """ Utility functions for topgen | |
| """ | |
| import logging as log | |
| import sys | |
| from collections import OrderedDict | |
| from mako.template import Template | |
| from typing import Dict, Tuple | |
| from reggen.ip_block import IpBlock | |
| from topgen import lib as lib | |
| from topgen.c import TopGenC | |
| from topgen.c_test import TopGenCTest | |
| class TopGenMatchaC(TopGenC): | |
| """ A class to add the Opentitan TopGenC with extra plic rendering methods. | |
| """ | |
| def __init__(self, top_info, name_to_block: Dict[str, IpBlock]): | |
| super().__init__(top_info, name_to_block) | |
| # Update plic CEnum objects with appended smc suffix in the name | |
| # for the first and last elements to avoid naming collision. | |
| self.plic_sources.constants[0][0].parts.append("smc") | |
| self.plic_sources.constants[-1][0].parts.append("smc") | |
| self.plic_targets.constants[0][0].parts.append("smc") | |
| self.plic_targets.constants[-1][0].parts.append("smc") | |
| self.plic_interrupts.constants[0][0].parts.append("smc") | |
| self.plic_interrupts.constants[-1][0].parts.append("smc") | |
| def smc_plic_sources_render(self): | |
| return self._smc_c_enum_render(self.plic_sources) | |
| def smc_plic_targets_render(self): | |
| return self._smc_c_enum_render(self.plic_targets) | |
| def smc_plic_interrupts_render(self): | |
| return self._smc_c_enum_render(self.plic_interrupts) | |
| def smc_plic_interrupts_define_render(self): | |
| return self._smc_c_define_render(self.plic_interrupts) | |
| def smc_plic_mapping_render_declaration(self): | |
| template = ( | |
| "extern const ${mapping.output_type_name.as_snake_case()}_smc_t\n" | |
| " ${mapping.name.as_snake_case()}_smc[${len(mapping.mapping)}];") | |
| return Template(template).render(mapping=self.plic_mapping) | |
| def smc_plic_mapping_render_definition(self): | |
| template = ( | |
| "const ${mapping.output_type_name.as_snake_case()}_smc_t\n" | |
| " ${mapping.name.as_snake_case()}_smc[${len(mapping.mapping)}] = {\n" | |
| "% for in_name, out_name in mapping.mapping.items():\n" | |
| " [${in_name.as_c_enum()}] = ${out_name.as_c_enum()},\n" | |
| "% endfor\n" | |
| "};\n") | |
| return Template(template).render(mapping=self.plic_mapping) | |
| def _smc_c_enum_render(self, enum): | |
| """ Add the c_helper CEnum rendering code for smc.""" | |
| template = ("typedef enum ${enum.name.as_snake_case()}_smc {\n" | |
| "% for name, value, docstring in enum.constants:\n" | |
| " ${name.as_c_enum()} = ${value}, /**< ${docstring} */\n" | |
| "% endfor\n" | |
| "} ${enum.name.as_snake_case()}_smc_t;") | |
| return Template(template).render(enum=enum) | |
| def _smc_c_define_render(self, enum): | |
| """ Add the c_helper CDefine rendering code for smc.""" | |
| template = ("% for name, value, docstring in enum.constants:\n" | |
| "#define ${name.as_c_define()} ${value} /**< ${docstring} */\n" | |
| "% endfor\n") | |
| return Template(template).render(enum=enum) | |
| def _separate_interrupt_modules(top: OrderedDict, name_to_block: Dict[str, | |
| IpBlock], | |
| intr_to_smc_list: list): | |
| """Populate top["interrupt_module"] if necessary | |
| Do this by adding each module in top['modules'] that defines at least one | |
| interrupt. | |
| """ | |
| if ("sec_interrupt_module" in top) or ("smc_interrupt_module" in top): | |
| return | |
| modules_sec = [] | |
| modules_smc = [] | |
| for module in top["module"]: | |
| block = name_to_block[module["type"]] | |
| if block.interrupts: | |
| if module["name"] in intr_to_smc_list: | |
| modules_smc.append(module["name"]) | |
| else: | |
| modules_sec.append(module["name"]) | |
| log.info("the genreate modules_sec is: ", modules_sec) | |
| top["sec_interrupt_module"] = modules_sec | |
| log.info("the genreate modules_smc is: ", modules_smc) | |
| top["smc_interrupt_module"] = modules_smc | |
| def create_smc_c_helper(helper: TopGenCTest, topcfg: OrderedDict, | |
| name_to_block: Dict[str, IpBlock]) -> TopGenCTest: | |
| """ Create the smc-specific c-code helper object. | |
| A workaround to append the smc-specific helper rendering functions in | |
| addition to the OpenTitan c_helper class. | |
| """ | |
| smc_helper = TopGenMatchaC(topcfg, name_to_block) | |
| helper.smc_helper = smc_helper | |
| return helper | |
| def create_separate_interrupt_topcfg( | |
| top: OrderedDict,) -> Tuple[OrderedDict, OrderedDict]: | |
| """ Create two topcfg dictionaries from separated interrupt groups. | |
| This is a workaround to use the Opentitan utility infrastructure to generate | |
| the c template helpers, which assumes one interrupt list at the top level. | |
| """ | |
| interrupt_group = ["sec_interrupt", "smc_interrupt", "interrupt"] | |
| if not all(x in top for x in interrupt_group): | |
| log.error( | |
| f"Need to have 'sec_interrupt', 'smc_interrupt, 'interrupt' keys " | |
| f"in topcfg") | |
| sys.exit(1) | |
| sec_topcfg = top.copy() | |
| sec_topcfg["interrupt"] = sec_topcfg["sec_interrupt"] | |
| sec_topcfg["interrupt_module"] = sec_topcfg["sec_interrupt_module"] | |
| smc_topcfg = top.copy() | |
| smc_topcfg["num_cores"] = 1 # used to define plic targets. | |
| smc_topcfg["interrupt"] = smc_topcfg["smc_interrupt"] | |
| smc_topcfg["interrupt_module"] = smc_topcfg["smc_interrupt_module"] | |
| return sec_topcfg, smc_topcfg | |
| def amend_separate_interrupt(top: OrderedDict, name_to_block: Dict[str, | |
| IpBlock], | |
| intr_to_smc_list: list) -> OrderedDict: | |
| """Check interrupt_module if exists, or just use all modules | |
| """ | |
| _separate_interrupt_modules(top, name_to_block, intr_to_smc_list) | |
| if "sec_interrupt" not in top or top["sec_interrupt"] == "": | |
| top["sec_interrupt"] = [] | |
| if "smc_interrupt" not in top or top["smc_interrupt"] == "": | |
| top["smc_interrupt"] = [] | |
| interrupt_module_list = [ | |
| top["sec_interrupt_module"], top["smc_interrupt_module"] | |
| ] | |
| interrupt_list = [top["sec_interrupt"], top["smc_interrupt"]] | |
| for idx, l in enumerate(interrupt_module_list): | |
| for m in l: | |
| ips = list(filter(lambda module: module["name"] == m, | |
| top["module"])) | |
| if len(ips) == 0: | |
| log.warning( | |
| "Cannot find IP %s which is used in the interrupt_module" % | |
| m) | |
| continue | |
| ip = ips[0] | |
| block = name_to_block[ip["type"]] | |
| log.info("Adding interrupts from module %s" % ip["name"]) | |
| for signal in block.interrupts: | |
| sig_dict = signal.as_nwt_dict('interrupt') | |
| qual = lib.add_module_prefix_to_signal(sig_dict, | |
| module=m.lower()) | |
| interrupt_list[idx].append(qual) | |
| return top |