|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  | """This contains a class which is used to help generate top level SW tests. | 
|  |  | 
|  | We maintain as a separate class rather than merge implementation with c.py, | 
|  | because both serve different purposes and need to capture the information | 
|  | differently. | 
|  | """ | 
|  |  | 
|  | import logging as log | 
|  | import sys | 
|  | from typing import Dict | 
|  |  | 
|  | from reggen.ip_block import IpBlock | 
|  |  | 
|  | from .c import TopGenC | 
|  | from .lib import Name | 
|  |  | 
|  |  | 
|  | class TestPeripheral: | 
|  | """Captures a peripheral instance's attributes for use in SW tests. | 
|  |  | 
|  | It encapsulates the settings for each peripheral's instance in the design. | 
|  | These settings are captured as identifiers that are already defined in | 
|  | other autogenerated / hand-edited sources such as top_{name}.h and the DIF | 
|  | headers, rather than hard-coded constants. This is done to ensure that a | 
|  | single source of truth is referenced in all of the tests. | 
|  | """ | 
|  | def __init__(self, name: str, inst_name: str, base_addr_name: str, | 
|  | is_templated: bool): | 
|  | self.name = name | 
|  | self.inst_name = inst_name | 
|  | self.base_addr_name = base_addr_name | 
|  | self.is_templated = is_templated | 
|  |  | 
|  |  | 
|  | class IrqTestPeripheral(TestPeripheral): | 
|  | """Captures a peripheral instance's attributes for use in IRQ test.""" | 
|  | def __init__(self, name: str, inst_name: str, base_addr_name: str, | 
|  | is_templated: bool, plic_name: str, start_irq: str, | 
|  | end_irq: str, plic_start_irq: str): | 
|  | super().__init__(name, inst_name, base_addr_name, is_templated) | 
|  | self.plic_name = plic_name | 
|  | self.start_irq = start_irq | 
|  | self.end_irq = end_irq | 
|  | self.plic_start_irq = plic_start_irq | 
|  |  | 
|  |  | 
|  | class AlertTestPeripheral(TestPeripheral): | 
|  | """Captures a peripheral instance's attributes for use in IRQ test.""" | 
|  | def __init__(self, name: str, inst_name: str, base_addr_name: str, | 
|  | is_templated: bool, top_alert_name: str, | 
|  | dif_alert_name: str, num_alerts: int): | 
|  | super().__init__(name, inst_name, base_addr_name, is_templated) | 
|  | self.top_alert_name = top_alert_name | 
|  | self.dif_alert_name = dif_alert_name | 
|  | self.num_alerts = num_alerts | 
|  |  | 
|  |  | 
|  | class TopGenCTest(TopGenC): | 
|  | def __init__(self, top_info, name_to_block: Dict[str, IpBlock]): | 
|  | super().__init__(top_info, name_to_block) | 
|  |  | 
|  | self.irq_peripherals = self._get_irq_peripherals() | 
|  | self.alert_peripherals = self._get_alert_peripherals() | 
|  |  | 
|  | def _get_irq_peripherals(self): | 
|  | irq_peripherals = [] | 
|  | self.devices() | 
|  | for entry in self.top['module']: | 
|  | inst_name = entry['name'] | 
|  | if inst_name not in self.top["interrupt_module"]: | 
|  | continue | 
|  |  | 
|  | name = entry['type'] | 
|  | plic_name = (self._top_name + Name(["plic", "peripheral"]) + | 
|  | Name.from_snake_case(inst_name)) | 
|  | plic_name = plic_name.as_c_enum() | 
|  |  | 
|  | # Device regions may have multiple TL interfaces. Pick the region | 
|  | # associated with the 'core' interface. | 
|  | if_name = None if len( | 
|  | self.device_regions[inst_name]) == 1 else 'core' | 
|  | try: | 
|  | region = self.device_regions[inst_name][if_name] | 
|  | except KeyError: | 
|  | log.error(f"The 'base_addrs' dict for {name} needs to have " | 
|  | "one entry keyed with 'None' or 'core'.") | 
|  | sys.exit(1) | 
|  |  | 
|  | base_addr_name = region.base_addr_name().as_c_define() | 
|  | is_templated = 'attr' in entry and entry['attr'] == 'templated' | 
|  |  | 
|  | plic_name = (self._top_name + Name(["plic", "peripheral"]) + | 
|  | Name.from_snake_case(inst_name)) | 
|  | plic_name = plic_name.as_c_enum() | 
|  |  | 
|  | start_irq = self.device_irqs[inst_name][0] | 
|  | end_irq = self.device_irqs[inst_name][-1] | 
|  | plic_start_irq = (self._top_name + Name(["plic", "irq", "id"]) + | 
|  | Name.from_snake_case(start_irq)) | 
|  | plic_start_irq = plic_start_irq.as_c_enum() | 
|  |  | 
|  | # Get DIF compliant, instance-agnostic IRQ names. | 
|  | start_irq = start_irq.replace(inst_name, f"dif_{name}_irq", 1) | 
|  | start_irq = Name.from_snake_case(start_irq).as_c_enum() | 
|  | end_irq = end_irq.replace(inst_name, f"dif_{name}_irq", 1) | 
|  | end_irq = Name.from_snake_case(end_irq).as_c_enum() | 
|  |  | 
|  | irq_peripheral = IrqTestPeripheral(name, inst_name, base_addr_name, | 
|  | is_templated, plic_name, start_irq, | 
|  | end_irq, plic_start_irq) | 
|  | irq_peripherals.append(irq_peripheral) | 
|  |  | 
|  | irq_peripherals.sort(key=lambda p: p.inst_name) | 
|  | return irq_peripherals | 
|  |  | 
|  | def _get_alert_peripherals(self): | 
|  | alert_peripherals = [] | 
|  | self.devices() | 
|  | for entry in self.top['module']: | 
|  | inst_name = entry['name'] | 
|  | if inst_name not in self.top["alert_module"]: | 
|  | continue | 
|  |  | 
|  | # RV_DM module does not have DIF. | 
|  | if inst_name == "rv_dm": | 
|  | continue | 
|  |  | 
|  | for item in self.top['module']: | 
|  | if item['name'] == inst_name: | 
|  | name = item['type'] | 
|  |  | 
|  | regions = self.device_regions[inst_name] | 
|  | if "core" in regions: | 
|  | if_name = "core" | 
|  | elif "regs" in regions: | 
|  | if_name = "regs" | 
|  | elif "cfg" in regions: | 
|  | if_name = "cfg" | 
|  | else: | 
|  | if_name = None | 
|  | region = regions[if_name] | 
|  | base_addr_name = region.base_addr_name().as_c_define() | 
|  | break | 
|  |  | 
|  | is_templated = 'attr' in entry and entry['attr'] == 'templated' | 
|  | dif_alert_name = self.device_alerts[inst_name][0] | 
|  | num_alerts = len(self.device_alerts[inst_name]) | 
|  |  | 
|  | top_alert_name = (self._top_name + | 
|  | Name(["Alert", "Id"]) + | 
|  | Name.from_snake_case(dif_alert_name)) | 
|  | top_alert_name = top_alert_name.as_c_enum() | 
|  |  | 
|  | # Get DIF compliant, instance-agnostic alert names. | 
|  | dif_alert_name = dif_alert_name.replace(inst_name, f"dif_{name}_alert", 1) | 
|  | dif_alert_name = Name.from_snake_case(dif_alert_name).as_c_enum() | 
|  |  | 
|  | alert_peripheral = AlertTestPeripheral(name, inst_name, base_addr_name, is_templated, | 
|  | top_alert_name, dif_alert_name, num_alerts) | 
|  | alert_peripherals.append(alert_peripheral) | 
|  |  | 
|  | alert_peripherals.sort(key=lambda p: p.inst_name) | 
|  | return alert_peripherals |