blob: eae6338c1e2630d3f40475fe4852908611920570 [file] [log] [blame]
# 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