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