blob: 84d3df66cb19d9f311daa08f1221a7531bd2298c [file] [log] [blame]
# Copyright 2022 Google.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
""" 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_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 _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