# Copyright 2024 Volvo Car Corporation # Licensed under Apache 2.0. # -*- coding: utf-8 -*- """Module containing cvc classes for VCC - defines and includes for memory sections.""" import time from pathlib import Path from powertrain_build import build_defs from powertrain_build.problem_logger import ProblemLogger class MemorySection(ProblemLogger): """Handle headers for CVC_* definitions.""" calibration_definitions = [ 'CVC_CAL', 'CVC_CAL_ASIL_A', 'CVC_CAL_ASIL_B', 'CVC_CAL_ASIL_C', 'CVC_CAL_ASIL_D', 'CVC_CAL_MERGEABLE_ASIL_A', 'CVC_CAL_MERGEABLE_ASIL_B', 'CVC_CAL_MERGEABLE_ASIL_C', 'CVC_CAL_MERGEABLE_ASIL_D' ] measurable_definitions = [ 'CVC_DISP', 'CVC_DISP_ASIL_A', 'CVC_DISP_ASIL_B', 'CVC_DISP_ASIL_C', 'CVC_DISP_ASIL_D' ] def __init__(self, build_cfg, unit_cfg): super().__init__() self.build_cfg = build_cfg self.unit_cfg = unit_cfg self.mem_map_config = self.build_cfg.get_memory_map_config() self.include_header_guards = self.mem_map_config['includeHeaderGuards'] self.mem_map_include = f'#include "{self.mem_map_config["memMapPrefix"]}_MemMap.h"\n' self.xcp_enabled = self.build_cfg.get_xcp_enabled() self.use_volatile_globals = self.build_cfg.get_use_volatile_globals() @staticmethod def _get_mem_map_section(section): return 'STOP' if section == 'END' else section @staticmethod def _get_header(section_file): section_file_header_guard = section_file.split('.')[0].upper() return [ f'#ifndef {section_file_header_guard}_H\n', f'#define {section_file_header_guard}_H\n\n' ] @staticmethod def _get_footer(section_file): section_file_header_guard = section_file.split('.')[0].upper() return [f'\n#endif /* {section_file_header_guard}_H */\n'] def _get_calibration_rte_macro_expansion(self, section_file): macros = self._get_header(section_file) swc_name = self.build_cfg.get_composition_config("softwareComponentName") macros.append(f'#include "Rte_{swc_name}.h"\n') config = self.unit_cfg.get_per_cfg_unit_cfg() valid_configs = ["outports", "local_vars", "calib_consts"] for valid_config in valid_configs: for signal_name, unit_info in config.get(valid_config, {}).items(): define_str = f'#define {signal_name} Rte_CData_{swc_name}_{signal_name}()' if signal_name.startswith("m") and not signal_name.endswith("_r") and not signal_name.endswith("_c"): define_str += f"->dt_{signal_name}" define_str += "\n" for info in unit_info.values(): # Should be length 1 for cal if "CVC_CAL" in info["class"]: macros.append(define_str) macros.extend(self._get_footer(section_file)) return macros def _get_cal(self, section): cvc_undefines = [f'#undef {definition}\n' for definition in self.calibration_definitions] if section == 'START': volatile_string = 'volatile' if self.use_volatile_globals else '' cvc_defines = [f'#define {definition} {volatile_string}\n' for definition in self.calibration_definitions] else: cvc_defines = [] section_type = 'cal' if self.xcp_enabled else 'disp' memory_section_handling = [ self.mem_map_config['projectDefines'][self._get_mem_map_section(section)][section_type] + '\n' ] if self.mem_map_config['includeMemMapForCalibration'] or not self.xcp_enabled: memory_section_handling.append(self.mem_map_include) return cvc_undefines, cvc_defines, memory_section_handling def _get_disp(self, section): cvc_undefines = [f'#undef {definition}\n' for definition in self.measurable_definitions] if section == 'START': volatile_string = 'volatile' if self.use_volatile_globals else '' cvc_defines = [f'#define {definition} {volatile_string}\n' for definition in self.measurable_definitions] else: cvc_defines = [] memory_section_handling = [ self.mem_map_config['projectDefines'][self._get_mem_map_section(section)]['disp'] + '\n', self.mem_map_include ] return cvc_undefines, cvc_defines, memory_section_handling def _get_code(self, section): cvc_undefines = [] cvc_defines = [] memory_section_handling = [ self.mem_map_config['projectDefines'][self._get_mem_map_section(section)]['code'] + '\n', self.mem_map_include ] return cvc_undefines, cvc_defines, memory_section_handling def _get_const(self, section): cvc_undefines = [] cvc_defines = [] memory_section_handling = [ self.mem_map_config['projectDefines'][self._get_mem_map_section(section)]['const'] + '\n', self.mem_map_include ] return cvc_undefines, cvc_defines, memory_section_handling def _get_nvm(self, section): cvc_undefines = [] cvc_defines = [] memory_section_handling = [ self.mem_map_config['projectDefines'][self._get_mem_map_section(section)]['nvm'] + '\n', self.mem_map_include ] return cvc_undefines, cvc_defines, memory_section_handling def _get_rest(self): cvc_undefines = [] cvc_defines = [] memory_section_handling = [ self.mem_map_include ] return cvc_undefines, cvc_defines, memory_section_handling def _get_predecl(self): cvc_undefines = [] cvc_defines = [] memory_section_handling = [ self.mem_map_include ] return cvc_undefines, cvc_defines, memory_section_handling def generate_cvc_header(self, section, section_file): """Generate CVC headers. Args: section (str): Name of the CVC section section_file (str): Name of the header file Returns: lines_to_write (list(str)): Lines to write to given section file. """ header = self._get_header(section_file) if self.include_header_guards else [] footer = self._get_footer(section_file) if self.include_header_guards else [] if '_CAL_' in section_file: cvc_undefines, cvc_defines, memory_section_handling = self._get_cal(section) elif '_DISP_' in section_file: cvc_undefines, cvc_defines, memory_section_handling = self._get_disp(section) elif not section_file.startswith('PREDECL_'): if section_file.startswith('CVC_CAL') or section_file.startswith('CVC_DISP'): self.critical('Should not find CVC_CAL/DISP here. Check logic. File: %s.', section_file) elif section_file.startswith('CVC_CODE'): cvc_undefines, cvc_defines, memory_section_handling = self._get_code(section) use_rte_macro_expansion = self.build_cfg.get_code_generation_config(item="useRteMacroExpansion") if section == 'START' and use_rte_macro_expansion: memory_section_handling.extend(self._get_calibration_rte_macro_expansion(section_file)) # header and footer are part of the extension header = [] footer = [] elif section_file.startswith('CVC_CONST'): cvc_undefines, cvc_defines, memory_section_handling = self._get_const(section) else: cvc_undefines, cvc_defines, memory_section_handling = self._get_rest() else: cvc_undefines, cvc_defines, memory_section_handling = self._get_predecl() return header + cvc_undefines + cvc_defines + memory_section_handling + footer def generate_required_header_files(self): """Generate required header files to delivery folder. Generate required header files such as memory protection files. NOTE: Currently, only one ASIL level can be selected for an SWC. """ self.info('******************************************************') self.info('Start generating required header files') start_time = time.time() src_dst_dir = self.build_cfg.get_src_code_dst_dir() for section_dict in build_defs.PREDECL_EXTRA.values(): for section_file in section_dict.values(): header = self._get_header(section_file) if self.include_header_guards else [] footer = self._get_footer(section_file) if self.include_header_guards else [] with Path(src_dst_dir, section_file).open('w', encoding="utf-8") as header_file_handler: header_file_handler.writelines(header + footer) for asil_dict in build_defs.ASIL_LEVEL_MAP.values(): for type_dict in asil_dict.values(): for section_dict in type_dict.values(): for section, section_file in section_dict.items(): lines_to_write = self.generate_cvc_header(section, section_file) with Path(src_dst_dir, section_file).open('w', encoding="utf-8") as header_file_handler: header_file_handler.writelines(lines_to_write) for nvm_type, nvm_dict in build_defs.NVM_LEVEL_MAP.items(): for section_dict in nvm_dict.values(): for section, section_file in section_dict.items(): section_config = self.mem_map_config['projectDefines'][self._get_mem_map_section(section)] if nvm_type == "NORMAL" and "nvm" in section_config: lines_to_write = self._get_nvm(section)[2] else: lines_to_write = [] header = self._get_header(section_file) if self.include_header_guards else [] footer = self._get_footer(section_file) if self.include_header_guards else [] with Path(src_dst_dir, section_file).open('w', encoding="utf-8") as header_file_handler: header_file_handler.writelines(header + lines_to_write + footer) self.info('Finished generating required header files (in %4.2f s)', time.time() - start_time)