65c1d746a7
We don't transfer git history since it may contain proprietary data that we cannot have in an open sources version. Change-Id: I9586124c1720db69a76b9390e208e9f0ba3b86d4
297 lines
11 KiB
Python
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
|
|
}
|