olindgre 2ece01e1d7 Make powertrain-build not overlap with pybuild in site-packages
Change-Id: I7b59f3f04f0f787d35db0b9389f295bf1ad24f56
2024-09-17 10:25:04 +02:00

250 lines
9.7 KiB
Python

# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.
# -*- coding: utf-8 -*-
"""Module containing classes for VCC - Supplier debug interface.
TODO: Check if all IO parameters in SPMEMSInterfaceRequirements.xlsx defined as safety variables
automatically have debug switches.
"""
import os
import re
from powertrain_build import build_defs
from powertrain_build.types import byte_size_string, get_bitmask, a2l_range
from powertrain_build.a2l import A2l
from powertrain_build.problem_logger import ProblemLogger
class ExtDbg(ProblemLogger):
"""Class for generating c-files.
These declares all debug parameters in the VCC - Supplier interface.
"""
__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, integrity_level=build_defs.CVC_ASIL_QM):
"""Constructor.
Args:
variable_dict (dict): dictionary with signal information
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': ...
}
The optional keyword arguments may override which code section
directives to use.
"""
super().__init__()
self.set_integrity_level(integrity_level)
self._var_dict = variable_dict
self.__restructured_data = self._restruct_data()
self._prj_cfg = prj_cfg
self._unit_cfg = unit_cfg
self.use_volatile_globals = prj_cfg.get_use_volatile_globals()
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['DISP']['START']
self._disp_end = integrity_level['DISP']['END']
self._cal_start = integrity_level['CAL']['START']
self._cal_end = integrity_level['CAL']['END']
self._code_start = integrity_level['CODE']['START']
self._code_end = integrity_level['CODE']['END']
def _restruct_data(self):
"""Restructure input variables per data-type.
This will be used for declaring the variables and generating the A2L-file.
"""
data = {'outputs': {}, 'inputs': {}}
for inp in self._var_dict.keys():
if re.match(r'.*Output$', inp) is not None:
iotype = 'outputs'
else:
iotype = 'inputs'
for var, var_data in self._var_dict[inp].items():
data_type_size = byte_size_string(var_data['type'])
data[iotype].setdefault(data_type_size, {})[var] = var_data
return data
def _a2l_dict(self, var_dict, function):
"""Generate dict defining parameters for a2l-generation."""
def _range(data):
range_a2l = a2l_range(data['type'])
if data['min'] == '-':
a2l_min = range_a2l[0]
else:
a2l_min = data['min']
if data['max'] == '-':
a2l_max = range_a2l[1]
else:
a2l_max = data['max']
return a2l_min, a2l_max
res = {'vars': {},
'function': function}
for var, data in self._type_order_iterator(var_dict):
resv = res['vars']
a2l_min, a2l_max = _range(data)
a2l_data = {
'bitmask': get_bitmask(data['type']),
'description': data['description'],
'lsb': '2^0',
'max': a2l_max,
'min': a2l_min,
'offset': '0',
'unit': '',
'x_axis': None,
'y_axis': None
}
var_db = f'c{var[1:]}_db'
var_sw = self._var_name_to_dbgsw_name(var)
resv.setdefault(var_db, {})['a2l_data'] = a2l_data
a2l_data = {
'bitmask': get_bitmask(data['type']),
'description': f'debug switch for {var_db} (1=bdsw act)',
'lsb': '2^0',
'max': '1',
'min': '0',
'offset': '0',
'unit': '',
'x_axis': None,
'y_axis': None
}
resv.setdefault(var_sw, {})['a2l_data'] = a2l_data
resv[var_db]['array'] = []
resv[var_sw]['array'] = []
resv[var_db]['function'] = [function]
resv[var_sw]['function'] = [function]
resv[var_db]['var'] = {'cvc_type': 'CVC_CAL',
'type': data['type'],
'var': var}
resv[var_sw]['var'] = {'cvc_type': 'CVC_CAL',
'type': 'Bool',
'var': var}
return res
@staticmethod
def _var_name_to_dbgsw_name(name):
"""Convert a variable name to a debug switch name."""
# the below conversion would generate a correct name for the
# debug switch, however the current build systemt generates one
# like the currently returned name
# return re.sub(r'\w(\w+?)_\w+?_(\w+)', r'c\1_B_\2_sw', name)
return re.sub(r'\w(\w+)', r'c\1_sw', name)
@staticmethod
def _type_order_iterator(var_items):
"""Get iterator over all variables.
In data type size order, and then in alphabetical order.
"""
for _, typ_data in var_items.items():
for var in sorted(typ_data.keys()):
yield (var, typ_data[var])
def _gen_dbg_c_file(self, data, filename):
"""Generate debug c-files.
These define all the debug labels for
the supplier input and output signals.
"""
with open(filename, 'w', encoding="utf-8") as fh_c:
fh_c.write(self._unit_cfg.base_types_headers)
fh_c.write('#define CVC_DISP\n')
# define extrern variable references
fh_c.write(f'#include "{self._disp_start}"\n')
for var, var_data in self._type_order_iterator(data):
fh_c.write(f"extern CVC_DISP {var_data['type']} {var};\n")
fh_c.write(f'#include "{self._disp_end}"\n\n')
# define debug calibration constants
fh_c.write(f'#include "{self._cal_start}"\n')
fh_c.write('\n/* Debug values */\n\n')
for var, var_data in self._type_order_iterator(data):
initial_value = var_data['min'] if var_data['min'] != "-" and float(var_data['min']) > 0 else "0"
if self.use_volatile_globals:
fh_c.write(f"volatile {var_data['type']} c{var[1:]}_db = {initial_value};\n")
else:
fh_c.write(f"{var_data['type']} c{var[1:]}_db = {initial_value};\n")
fh_c.write('\n/* Debug switches */\n\n')
for var, var_data in self._type_order_iterator(data):
sw_name = self._var_name_to_dbgsw_name(var)
if self.use_volatile_globals:
fh_c.write(f"volatile Bool {sw_name} = 0;\n")
else:
fh_c.write(f"Bool {sw_name} = 0;\n")
fh_c.write(f'#include "{self._cal_end}"\n\n')
# set the variable to the debug calibration constants
fh_c.write('/***********************/\n')
fh_c.write('/* debug functionality */\n')
fh_c.write('/***********************/\n\n')
_, fname_tmp = os.path.split(filename)
func_name = fname_tmp.split('.')[-2]
fh_c.write(f'#include "{self._code_start}"\n')
fh_c.write(f'void {func_name}(void) {{\n')
for var, var_data in self._type_order_iterator(data):
sw_name = self._var_name_to_dbgsw_name(var)
fh_c.write(f' if ({sw_name}) {{\n')
fh_c.write(f' {var} = c{var[1:]}_db;\n }}\n')
fh_c.write(f'}}\n#include "{self._code_end}"\n\n')
self.info('Generated %s', filename)
def gen_dbg_files(self, fname_in, fname_out):
"""Generate the c-files and A2L-files.
These declares all the supplier interface debug parameters and functions.
"""
# c-files
self._gen_dbg_c_file(self.__restructured_data['inputs'],
fname_in + '.c')
self._gen_dbg_c_file(self.__restructured_data['outputs'],
fname_out + '.c')
# A2L-files
_, fname_tmp = os.path.split(fname_in)
a2l_dict_in = self._a2l_dict(self.__restructured_data['inputs'],
fname_tmp)
_, fname_tmp = os.path.split(fname_out)
a2l_dict_out = self._a2l_dict(self.__restructured_data['outputs'],
fname_tmp)
a2l = A2l(a2l_dict_in, self._prj_cfg)
a2l.gen_a2l(fname_in + '.a2l')
a2l = A2l(a2l_dict_out, self._prj_cfg)
a2l.gen_a2l(fname_out + '.a2l')