250 lines
9.7 KiB
Python
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')
|