| # 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 |