powertrain-build/pybuild/ext_var.py
Henrik Wahlqvist 65c1d746a7 Copy from Volvo Cars local project
We don't transfer git history since it may contain proprietary data that
we cannot have in an open sources version.

Change-Id: I9586124c1720db69a76b9390e208e9f0ba3b86d4
2024-05-29 08:03:54 +02:00

297 lines
11 KiB
Python

# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.
# -*- coding: utf-8 -*-
"""Module containing classes for VCC - Supplier signal interface."""
from pybuild import build_defs
from pybuild.types import byte_size_string, get_bitmask
from pybuild.a2l import A2l
from pybuild.problem_logger import ProblemLogger
class ExtVarBase(ProblemLogger):
"""Generate a2l- and c-files.
These which declares all variables in the interface that the
supplier platform writes to.
This is needed due to legacy handling of the interfaces between units.
Note that all variables sent from the VCC SPM to the platform should be declared in
the function that produces the signal!
"""
INPORT_INDEX = 0
OUTPORT_INDEX = 1
__data_type_size = {'Float32': '4', 'UInt32': '4', 'Int32': '4',
'UInt16': '2', 'Int16': '2',
'UInt8': '1', 'Int8': '1', 'Bool': '1'}
def __init__(self, variable_dict, prj_cfg, unit_cfg, user_defined_types, integrity_level=build_defs.ASIL_QM):
"""Constructor.
Args:
variable_dict (dict): dictionary with signal information.
prj_cfg (BuildProjConfig): Build project class holding where files should be stored.
user_defined_types (UserDefinedTypes): Class holding user defined data types.
integrity_level (str): integrity level of the unit from 'A' to 'D' or 'QM'.
"""
super().__init__()
self.set_integrity_level(integrity_level)
self._var_dict = variable_dict
self._ext_vars = {}
self._prj_cfg = prj_cfg
self._unit_cfg = unit_cfg
self._enumerations = user_defined_types.get_enumerations()
self._common_header_files = user_defined_types.common_header_files
def set_integrity_level(self, integrity_level):
"""Set integrity level of code generation.
Args:
integrity_level (str): integrity level of the unit from 'A' to 'D' or 'QM'
"""
self._disp_start = integrity_level['CVC']['DISP']['START']
self._disp_end = integrity_level['CVC']['DISP']['END']
self._decl_start = integrity_level['PREDECL']['DISP']['START']
self._decl_end = integrity_level['PREDECL']['DISP']['END']
def _get_byte_size_string(self, data_type):
"""Get byte size of a data type as string.
Enumeration byte sizes are derived from the underlying data type.
Args:
data_type (str): Data type.
Returns:
byte_size_string(pybuild.types.byte_size_string): Return result of pybuild.types.byte_size_string.
"""
if data_type in self._enumerations:
return byte_size_string(self._enumerations[data_type]['underlying_data_type'])
return byte_size_string(data_type)
def _get_bitmask(self, data_type):
"""Get bitmask of a data type.
Enumeration bitmasks are derived from the underlying data type.
Args:
data_type (str): Data type.
Returns:
get_bitmask(pybuild.types.get_bitmask): Return result of pybuild.types.get_bitmask.
"""
if data_type in self._enumerations:
return get_bitmask(self._enumerations[data_type]['underlying_data_type'])
return get_bitmask(data_type)
def _restruct_input_data(self):
"""Restructure all the input variables per data-type.
This will be used for declaring the variables and generating the
A2L-file
"""
external_inports = {}
external_outports = {}
for external_port_type in self.EXTERNAL_INPORT_TYPES:
if external_port_type in self._var_dict:
for var, data in self._var_dict[external_port_type].items():
data_type_size = self._get_byte_size_string(data[self.TYPE_NAME])
external_inports.setdefault(data_type_size, {})[var] = data
for external_port_type in self.EXTERNAL_OUTPORT_TYPES:
if external_port_type in self._var_dict:
for var, data in self._var_dict[external_port_type].items():
data_type_size = self._get_byte_size_string(data[self.TYPE_NAME])
external_outports.setdefault(data_type_size, {})[var] = data
self._ext_vars = external_inports, external_outports
def _a2l_dict(self):
"""Return a dict defining all parameters for a2l-generation."""
res = {
'vars': {},
'function': 'VcExtVar'
}
for inp in self.EXTERNAL_INPORT_TYPES:
if inp in self._var_dict:
for var, data in self._var_dict[inp].items():
if data[self.TYPE_NAME] in self._enumerations:
data_type = self._enumerations[data[self.TYPE_NAME]]['underlying_data_type']
else:
data_type = data[self.TYPE_NAME]
resv = res['vars']
resv.setdefault(var, {})['a2l_data'] = self.get_a2l_format(data)
resv[var]['array'] = []
resv[var]['function'] = ['VcEc']
resv[var]['var'] = {
'cvc_type': 'CVC_DISP',
'type': data_type,
'var': var
}
return res
def _generate_c_file(self, path):
"""Generate the c-file defining all the supplier input signals."""
header = path.with_suffix('.h').name
var_set = set()
with path.open('w') as fh_c:
fh_c.write(f'#include "{header}"\n')
fh_c.write(f'#include "{self._disp_start}"\n\n')
for data_type_s, ext_vars in self._ext_vars[self.INPORT_INDEX].items():
fh_c.write(f"/* Variables of size {data_type_s} bytes */\n\n")
for var in sorted(ext_vars.keys()):
data = ext_vars[var]
if var not in var_set:
fh_c.write(f"CVC_DISP {data[self.TYPE_NAME]} {var} = {data['init']};\n")
var_set.add(var)
fh_c.write('\n')
fh_c.write(f'\n#include "{self._disp_end}"\n')
self.info('Generated %s', path.name)
def _generate_h_file(self, path):
"""Generate header file externally declaring interface signals."""
filename = path.stem
guard = f"{filename.upper()}_H"
var_set = set()
with path.open('w') as fh_c:
fh_c.write(f'#ifndef {guard}\n')
fh_c.write(f'#define {guard}\n')
fh_c.write('#define CVC_DISP\n')
fh_c.write(self._unit_cfg.base_types_headers)
for common_header_file in self._common_header_files:
fh_c.write(f'#include "{common_header_file}"\n')
fh_c.write('\n')
fh_c.write(f'#include "{self._decl_start}"\n')
fh_c.write('/* VCC Inports */\n')
for data_type_s, ext_vars in self._ext_vars[self.INPORT_INDEX].items():
fh_c.write(f"/* Variables of size {data_type_s} bytes */\n\n")
for var in sorted(ext_vars.keys()):
if var not in var_set:
data = ext_vars[var]
fh_c.write(f"extern CVC_DISP {data[self.TYPE_NAME]} {var};\n")
var_set.add(var)
fh_c.write('\n')
fh_c.write('/* VCC Outports */\n')
for data_type_s, ext_vars in self._ext_vars[self.OUTPORT_INDEX].items():
fh_c.write(f"/* Variables of size {data_type_s} bytes */\n\n")
for var in sorted(ext_vars.keys()):
if var not in var_set:
data = ext_vars[var]
fh_c.write(f"extern CVC_DISP {data[self.TYPE_NAME]} {var};\n")
var_set.add(var)
fh_c.write('\n')
fh_c.write(f'#include "{self._decl_end}"\n')
fh_c.write('#endif\n')
self.info('Generated %s', path.name)
def generate_files(self, path):
"""Generate the c- and a2l-file for defining all the supplier input variables."""
self._restruct_input_data()
if not self._ext_vars[0] and not self._ext_vars[1]:
self.info(f'Skipping {path.name} as there were no corresponding vars.')
return
self._generate_c_file(path.with_suffix('.c'))
self._generate_h_file(path.with_suffix('.h'))
a2l_dict = self._a2l_dict()
a2l = A2l(a2l_dict, self._prj_cfg)
a2l.gen_a2l(path.with_suffix('.a2l'))
class ExtVarCsv(ExtVarBase):
"""Handles variable dicts from CSV files.
Variable dict shall have the following format and is generated by the
:doc:`CsvSignalInterfaces <signal_interfaces>` class::
{
'CAN-Input': {
'signal1': {
'IOType': 'd',
'description': 'Some description',
'init': 0,
'max': 1,
'min': 0,
'type': 'UInt8',
'unit': '-'
},
'signal2': {
...
}
},
'CAN-Output': {
'signal3': {
...
}
},
'xxx-Input': ...,
'xxx-Output': ...
}
"""
EXTERNAL_INPORT_TYPES = ['EMS-Input', 'CAN-Input', 'Private CAN-Input', 'LIN-Input']
EXTERNAL_OUTPORT_TYPES = ['EMS-Output', 'CAN-Output', 'Private CAN-Output', 'LIN-Output']
TYPE_NAME = 'type'
def get_a2l_format(self, data):
"""Get a2l format.
Args:
data (dict): Data dictionary.
Returns:
dict: A2l format dictionary.
"""
return {
'bitmask': self._get_bitmask(data[self.TYPE_NAME]),
'description': data['description'],
'lsb': '2^0',
'max': data['max'],
'min': data['min'],
'offset': '0',
'unit': data['unit'],
'x_axis': None,
'y_axis': None
}
class ExtVarYaml(ExtVarBase):
"""Handles variable dicts from Yaml files.
Variable dict shall have the following format and is generated by the
:doc:`YamlSignalInterfaces <signal_interfaces>` class::
{
'input': {
'sVcIhfa_D_WhlMotSysFrntLimnIndcn': {},
'sVcIhfa_D_WhlMotSysFrntModSts': {},
'sVcIhfa_I_WhlMotSysFrntIdc': {},
'sVcIhfa_U_UDcDcActHiSide1': {},
'sVcIhfa_U_WhlMotSysFrntUdc': {}
},
'output': {},
'status': {},
}
"""
EXTERNAL_INPORT_TYPES = ['input', 'status']
EXTERNAL_OUTPORT_TYPES = ['output']
TYPE_NAME = 'variable_type'
def get_a2l_format(self, data):
"""Get a2l format.
Args:
data (dict): Data dictionary.
Returns:
dict: A2l format dictionary.
"""
return {
'bitmask': self._get_bitmask(data[self.TYPE_NAME]),
'description': data['description'],
'lsb': '2^0',
'max': data['range']['max'],
'min': data['range']['min'],
'offset': '0',
'unit': data['unit'],
'x_axis': None,
'y_axis': None
}