Fix in ZC NVM generation

We need to use the struct definitions from the RTE.

Change-Id: I6e380ab5a153cd725e229fe03bffc8887105eed4
This commit is contained in:
Henrik Wahlqvist 2024-12-19 11:01:45 +01:00
parent 4dbb27b924
commit ef521427c8
7 changed files with 193 additions and 144 deletions
docs
powertrain_build
test_data/zone_controller/test_composition_yaml
tests
powertrain_build
zone_controller

@ -123,10 +123,11 @@ This key is used to set individual options to match the old ECU types, see examp
"generateInterfaceHeaders": false,
"generateRteCheckpointIds": false,
"generateYamlInterfaceFile": false,
"includeAllEnums": false,
"mapToRteEnums": false,
"propagateTagName": true,
"useA2lSymbolLinks": true,
"mapToRteEnums": false,
"includeAllEnums": false
"useRteNvmStructs": false
}
}
}
@ -181,6 +182,16 @@ Add RTE checkpoint ID definitions to the generated scheduling functions.
Generate a yaml file containing all metadata for the project.
Default is False.
#### includeAllEnums
Include all enums found in the location pointed at by enumDefDir, even if the enums are not in use in the project.
#### mapToRteEnums
Map TL generated enum values in source code (all caps) to RTE style enums (camel case enum members defined as ints)
when those are defined, instead of defining the enum class from its TL-counterpart.
This overwrites how the enum members are mapped to integers as defined in the project.
#### propagateTagName
Add the Git tag to the file "vcc_sp_version.h". Currently only used for old Volvo projects.
@ -191,15 +202,10 @@ Default is False.
Use "SYMBOL_LINK" in the generated A2L file.
Default is False.
#### mapToRteEnums
#### useRteNvmStructs
Map TL generated enum values in source code (all caps) to RTE style enums (camel case enum members defined as ints) when
those are defined, instead of defining the enum class from its TL-counterpart. This overwrites how the enum members are
mapped to integers as defined in the project.
#### includeAllEnums
Include all enums found in the location pointed at by enumDefDir, even if the enums are not in use in the project.
Use the NVM struct definitions generated by the RTE instead of generating it ourselves.
Default is False.
## Rasters json File

@ -72,10 +72,11 @@ class BuildProjConfig:
'generateInterfaceHeaders': False,
'generateRteCheckpointIds': False,
'generateYamlInterfaceFile': False,
'includeAllEnums': False,
'mapToRteEnums': False,
'propagateTagName': False,
'useA2lSymbolLinks': False,
'mapToRteEnums': False,
'includeAllEnums': False
'useRteNvmStructs': False,
}
def _get_code_generation_config(self):

@ -62,6 +62,7 @@ class NVMDef(ProblemLogger):
self._file_name = os.path.join(src_file_dst, self._nvm_defs["fileName"])
self._predecl_start = bd.PREDECL_START
self._predecl_end = bd.PREDECL_END
self.struct_member_prefix = "_"
with open(
os.path.join(project_config.get_root_dir(), self._nvm_defs["baseNvmStructs"]), "r", encoding="utf-8"
@ -156,10 +157,7 @@ class NVMDef(ProblemLogger):
yield nvm_attributes.get("type"), nvm_signal_name, nvm_attributes.get("width", 1)
def _a2l_dict(self):
"""Return a dict defining all parameters for a2l-generation.
Optionally, also patch the defined variables with the "schedulerPrefix".
"""
"""Return a dict defining all parameters for a2l-generation."""
res = {}
prefix = self._project_config.get_scheduler_prefix()
for var, var_attrib in self._nvm_signals.items():
@ -211,6 +209,7 @@ class NVMDef(ProblemLogger):
return widths
def _assert_data_type(self, signal, memory_area):
"""Asserts that each signal inside an NVM block is of one of the allowed data types of the block."""
memory_area_index = self._get_nvm_areas_index(memory_area)
if "NotApplicable" in self.nvm_definitions[memory_area_index]["allowed_datatypes"]:
# Special case for NVM_LIST_CRITICAL1 and 2. Any type is acceptable.
@ -444,84 +443,84 @@ class NVMDef(ProblemLogger):
with open(nvm_structs_updated_path, "w", encoding="utf-8") as nvm_structs_file:
json.dump(self.nvm_definitions, nvm_structs_file, indent=4)
def _generate_nvm_config_headers(self):
"""Generate nvm config h file.
def _get_signal_and_struct_defines(self):
"""Get NVM signal and struct defines."""
prefix = self._project_config.get_scheduler_prefix()
defines = []
struct_defines = []
for memory_area in self._nvm_memory_areas:
memory_area_with_prefix = f"{prefix}{memory_area}"
memory_area_index = self._get_nvm_areas_index(memory_area)
section = self._area_section[memory_area]
elem_size = self._mem_area_elem_size[memory_area]
struct_defines.append(f'#include "{section}_START.h"\n')
struct_defines.append(f"struct {prefix}{memory_area} {{\n")
Optionally, also patch the defined variables with the "schedulerPrefix".
"""
struct_off = 0
signals = self.nvm_definitions[memory_area_index]["signals"]
for signal in signals:
self._assert_data_type(signal, memory_area)
struct_defines.append(f' {signal["type"]:7} {self.struct_member_prefix}{signal["name"]}')
size_string = ""
size = max(signal["x_size"], 1) * max(signal["y_size"], 1)
if size > 1:
if signal["x_size"] > 1:
size_string += f'[{signal["x_size"]}]'
if signal["y_size"] > 1:
if signal["x_size"] < 2:
self.critical("NVM signal size incorrect. x_size should not be 1 if y_size > 1")
size_string += "[1]"
size_string += f'[{signal["y_size"]}]'
struct_defines.append(size_string)
struct_defines.append(";\n")
if signal["name"] in self._nvm_signals:
self._nvm_signals[signal["name"]]["struct_off"] = struct_off
struct_off += size * byte_size(signal["type"])
defines.append(
"#define "
f"{signal['name']:40} "
f"{memory_area_with_prefix.lower()}.{self.struct_member_prefix}{signal['name']}\n"
)
max_nr_signals = self.nvm_definitions[memory_area_index]["size"]
tot_memory = max_nr_signals * elem_size
free = tot_memory - struct_off
if free > 0:
struct_defines.append(
f' {self.nvm_definitions[memory_area_index]["default_datatype"]} '
f'unused[{int(free / elem_size)}];\n'
)
elif free < 0:
self.critical("NVM area %s overrun!", self.nvm_definitions[memory_area_index]["name"])
struct_defines.append(f"}}; /* {struct_off} bytes used of {tot_memory} */\n")
struct_defines.append(f'#include "{section}_END.h"\n\n')
return defines, struct_defines
def _generate_nvm_config_headers(self):
"""Generate nvm config h file."""
self.info("Start generating nvm header file")
prefix = self._project_config.get_scheduler_prefix()
defines, struct_defines = self._get_signal_and_struct_defines()
def write_signals():
for memory_area in self._nvm_memory_areas:
memory_area_index = self._get_nvm_areas_index(memory_area)
section = self._area_section[memory_area]
elem_size = self._mem_area_elem_size[memory_area]
hptr.write(f'#include "{section}_START.h"\n')
hptr.write(f"struct {prefix}{memory_area} {{\n")
struct_off = 0
signals = self.nvm_definitions[memory_area_index]["signals"]
for signal in signals:
self._assert_data_type(signal, memory_area)
hptr.write(f' {signal["type"]:7} _{signal["name"]}')
size_string = ""
size = max(signal["x_size"], 1) * max(signal["y_size"], 1)
if size > 1:
if signal["x_size"] > 1:
size_string += f'[{signal["x_size"]}]'
if signal["y_size"] > 1:
if signal["x_size"] < 2:
self.critical("NVM signal size incorrect. x_size should not be 1 if y_size > 1")
size_string += "[1]"
size_string += f'[{signal["y_size"]}]'
hptr.write(size_string)
hptr.write(";\n")
defines.append((signal["name"], f"{prefix.lower()}{memory_area.lower()}._{signal['name']}"))
if signal["name"] in self._nvm_signals:
self._nvm_signals[signal["name"]]["struct_off"] = struct_off
struct_off += size * byte_size(signal["type"])
max_nr_signals = self.nvm_definitions[memory_area_index]["size"]
tot_memory = max_nr_signals * elem_size
free = tot_memory - struct_off
if free > 0:
hptr.write(
f' {self.nvm_definitions[memory_area_index]["default_datatype"]} '
f'unused[{int(free / elem_size)}];\n'
)
elif free < 0:
self.critical("NVM area %s overrun!", self.nvm_definitions[memory_area_index]["name"])
hptr.write(f"}}; /* {struct_off} bytes used of {tot_memory} */\n")
hptr.write(f'#include "{section}_END.h"\n\n')
def write_extern():
hptr.write(f'#include "{self._predecl_start}"\n')
for area in self._nvm_memory_areas:
hptr.write(f"extern struct {prefix}{area.upper()} {prefix.lower()}{area.lower()};\n")
hptr.write(f'#include "{self._predecl_end}"\n\n')
def write_defines():
hptr.write("\n")
for var, define in defines:
hptr.write(f"#define {var:40} {define}\n")
defines = []
externals = [f'#include "{self._predecl_start}"\n']
for area in self._nvm_memory_areas:
externals.append(f"extern struct {prefix}{area.upper()} {prefix.lower()}{area.lower()};\n")
externals.append(f'#include "{self._predecl_end}"\n\n')
with open(self._file_name + ".h", "w", encoding="utf-8") as hptr:
hptr.write(self._nvm_header_head)
write_signals()
write_extern()
write_defines()
hptr.writelines(struct_defines)
hptr.writelines(externals)
hptr.write("\n")
hptr.writelines(defines)
hptr.write(self._nvm_header_footer)
def _generate_nvm_config_source(self):
"""Generate the c-file containing the NVM definition.
Optionally, also patch the defined variables with the "schedulerPrefix".
"""
"""Generate the c-file containing the NVM definition."""
# TODO: Add memory from previous builds!!! and mark old positions #
self.info("Start generating nvm source file")
prefix = self._project_config.get_scheduler_prefix()
@ -569,6 +568,7 @@ class ZCNVMDef(NVMDef):
nvm_vars (dict): NVM variables from unit configurations.
"""
super().__init__(project_config, unit_cfg, nvm_vars)
self.struct_member_prefix = "e_"
self._valid_nvm_definitions = None
self._update_nvm_base_struct()
prefix = self._project_config.get_scheduler_prefix()
@ -607,7 +607,10 @@ class ZCNVMDef(NVMDef):
def _update_header_and_footer(self):
name = self._project_config.get_composition_config("softwareComponentName")
use_rte_nvm_structs = self._project_config.get_code_generation_config("useRteNvmStructs")
self._nvm_header_head += f'#include "Rte_{name}.h"\n'
if use_rte_nvm_structs:
self._nvm_header_head += '#include "Rte_Type.h"\n\n'
self._nvm_header_footer = (
"\n"
f'#include "{bd.PREDECL_CODE_ASIL_D_START}"\n'
@ -617,6 +620,34 @@ class ZCNVMDef(NVMDef):
f"{self._nvm_header_footer}"
)
def _generate_rte_type_nvm_config_headers(self):
"""Generate NVM config header file using RTE struct definitions."""
self.info("Start generating nvm header file")
defines = self._get_signal_and_struct_defines()[0]
externals = [f'#include "{self._predecl_start}"\n']
for area in self.valid_nvm_definitions.keys():
externals.append(f"extern dt_{area} {area.lower()};\n")
externals.append(f'#include "{self._predecl_end}"\n\n')
with open(self._file_name + ".h", "w", encoding="utf-8") as hptr:
hptr.write(self._nvm_header_head)
hptr.writelines(externals)
hptr.writelines(defines)
hptr.write(self._nvm_header_footer)
def _generate_rte_type_nvm_config_source(self):
"""Generate the c-file containing the NVM definition using RTE struct definitions."""
# TODO: Add memory from previous builds!!! and mark old positions #
self.info("Start generating nvm source file")
prefix = self._project_config.get_scheduler_prefix()
with open(self._file_name + ".c", "w", encoding="utf-8") as cptr:
cptr.write(f'#include "{self._nvm_defs["fileName"]}.h"\n\n')
for area, pragma in zip(self._nvm_memory_areas, self._mem_area_pragmas):
memory_area_with_prefix = f"{prefix}{area}"
if memory_area_with_prefix in self.valid_nvm_definitions:
cptr.write(f'#include "{pragma[0]}"\n')
cptr.write(f"dt_{memory_area_with_prefix} {memory_area_with_prefix.lower()};\n")
cptr.write(f'#include "{pragma[1]}"\n\n')
def _append_nvm_rte_function_calls(self):
"""Append the NVM RTE function calls to the NVM config source file."""
nvm_port_pattern = self._project_config.get_composition_config("nvmPortPattern")
@ -665,8 +696,14 @@ class ZCNVMDef(NVMDef):
self.critical('Valid NVM definitions not set. Cannot generate NVM RTE files.')
return
use_rte_nvm_structs = self._project_config.get_code_generation_config("useRteNvmStructs")
self._update_header_and_footer()
self._generate_nvm_structs_updated()
self._generate_nvm_config_headers()
self._generate_nvm_config_source()
if use_rte_nvm_structs:
self._generate_rte_type_nvm_config_headers()
self._generate_rte_type_nvm_config_source()
else:
self._generate_nvm_config_headers()
self._generate_nvm_config_source()
self._append_nvm_rte_function_calls()

@ -354,6 +354,7 @@ class CompositionYaml(ProblemLogger):
data_type_name = f"dt_{nvm_name}"
data_types[data_type_name] = {"type": "RECORD", "elements": {}}
for signal in self.zc_nvm.project_nvm_definitions[nvm_name]["signals"]:
element_name = f'{self.zc_nvm.struct_member_prefix}{signal["name"]}'
nr_of_unused_signals -= signal["x_size"] * signal["y_size"]
size = max(signal["x_size"], 1) * max(signal["y_size"], 1)
if size > 1:
@ -361,7 +362,7 @@ class CompositionYaml(ProblemLogger):
y_data_type_name = f"dt_{signal['name']}_y"
if signal["x_size"] > 1 and signal["y_size"] == 1:
init.append([0] * signal["x_size"])
data_types[data_type_name]["elements"][signal["name"]] = x_data_type_name
data_types[data_type_name]["elements"][element_name] = x_data_type_name
data_types[x_data_type_name] = {
"type": "ARRAY",
"size": signal["x_size"],
@ -369,7 +370,7 @@ class CompositionYaml(ProblemLogger):
}
elif signal["x_size"] > 1 and signal["y_size"] > 1:
init.append([[0] * signal["y_size"]] * signal["x_size"])
data_types[data_type_name]["elements"][signal["name"]] = y_data_type_name
data_types[data_type_name]["elements"][element_name] = y_data_type_name
data_types[y_data_type_name] = {
"type": "ARRAY",
"size": signal["y_size"],
@ -384,7 +385,7 @@ class CompositionYaml(ProblemLogger):
self.critical("NVM signal size incorrect. x_size should not be 1 if y_size > 1.")
else:
init.append(0)
data_types[data_type_name]["elements"][signal["name"]] = signal["type"]
data_types[data_type_name]["elements"][element_name] = signal["type"]
nvm_data.update({
"datatype": data_type_name,
@ -397,6 +398,7 @@ class CompositionYaml(ProblemLogger):
}
if nr_of_unused_signals > 0:
# Mimics how we generate the unused member of the structs in nvm_def.py
nvm_data["init"].append([0] * nr_of_unused_signals)
data_types[data_type_name]["elements"]["unused"] = f"{data_type_name}_Unused"
data_types[f"{data_type_name}_Unused"] = {

@ -73,9 +73,9 @@ additional_data_types = {
"dt_prefix_NVM_LIST_8": {
"type": "RECORD",
"elements": {
"sVcModelName_X_DummyNvmSignal": "UInt8",
"sVcModelName_X_DummyNvmSignal2": "dt_sVcModelName_X_DummyNvmSignal2_x",
"sVcModelName_X_DummyNvmSignal3": "dt_sVcModelName_X_DummyNvmSignal3_y",
"e_sVcModelName_X_DummyNvmSignal": "UInt8",
"e_sVcModelName_X_DummyNvmSignal2": "dt_sVcModelName_X_DummyNvmSignal2_x",
"e_sVcModelName_X_DummyNvmSignal3": "dt_sVcModelName_X_DummyNvmSignal3_y",
"unused": "dt_prefix_NVM_LIST_8_Unused",
},
},

@ -171,61 +171,63 @@ class TestNVMDef(unittest.TestCase):
self.nvm_def._generate_nvm_config_a2l()
mock_open_file().__enter__().write.assert_called_once_with(expected)
@patch('builtins.open', new_callable=mock_open())
def test_generate_nvm_config_headers_patch(self, mock_open_file):
def test_generate_nvm_config_headers_patch(self):
"""Test nvm_def.NVMDef._generate_nvm_config_headers with prefix."""
self.proj_cnfg.get_scheduler_prefix = MagicMock(return_value='DummySwc_')
expected = [
call(
'/*\n * vcc_nvm_struct.h - struct for NVM signals\n */\n\n'
'#ifndef VCC_NVM_STRUCT_H\n'
'#define VCC_NVM_STRUCT_H\n\n'
'#include "tl_basetypes.h"\n'
),
call('#include "CVC_NVM_START.h"\n'),
call('struct DummySwc_NVM_LIST_8 {\n'),
call(' UInt8 unused[56];\n'),
call('}; /* 0 bytes used of 56 */\n'),
call('#include "CVC_NVM_END.h"\n\n'),
call('#include "CVC_NVM_START.h"\n'),
call('struct DummySwc_NVM_LIST_16 {\n'),
call(' UInt16 unused[240];\n'),
call('}; /* 0 bytes used of 480 */\n'),
call('#include "CVC_NVM_END.h"\n\n'),
call('#include "CVC_NVM_START.h"\n'),
call('struct DummySwc_NVM_LIST_32 {\n'),
call(' UInt32 unused[975];\n'),
call('}; /* 0 bytes used of 3900 */\n'),
call('#include "CVC_NVM_END.h"\n\n'),
call('#include "CVC_NVM_P_START.h"\n'),
call('struct DummySwc_NVM_LIST_8_PER {\n'),
call(' UInt8 unused[56];\n'),
call('}; /* 0 bytes used of 56 */\n'),
call('#include "CVC_NVM_P_END.h"\n\n'),
call('#include "CVC_NVM_P_START.h"\n'),
call('struct DummySwc_NVM_LIST_16_PER {\n'),
call(' UInt16 unused[240];\n'),
call('}; /* 0 bytes used of 480 */\n'),
call('#include "CVC_NVM_P_END.h"\n\n'),
call('#include "CVC_NVM_P_START.h"\n'),
call('struct DummySwc_NVM_LIST_32_PER {\n'),
call(' UInt32 unused[190];\n'),
call('}; /* 0 bytes used of 760 */\n'),
call('#include "CVC_NVM_P_END.h"\n\n'),
call('#include "PREDECL_START.h"\n'),
call('extern struct DummySwc_NVM_LIST_8 dummyswc_nvm_list_8;\n'),
call('extern struct DummySwc_NVM_LIST_16 dummyswc_nvm_list_16;\n'),
call('extern struct DummySwc_NVM_LIST_32 dummyswc_nvm_list_32;\n'),
call('extern struct DummySwc_NVM_LIST_8_PER dummyswc_nvm_list_8_per;\n'),
call('extern struct DummySwc_NVM_LIST_16_PER dummyswc_nvm_list_16_per;\n'),
call('extern struct DummySwc_NVM_LIST_32_PER dummyswc_nvm_list_32_per;\n'),
call('#include "PREDECL_END.h"\n\n'),
call('\n'),
call('\n#endif /* VCC_NVM_STRUCT_H */\n')
]
self.nvm_def._nvm_signals = self.small_nvm_struct
self.nvm_def._generate_nvm_config_headers()
mock_open_file().__enter__().write.assert_has_calls(expected)
result = []
m_open = mock_open()
m_open.return_value.write = result.append
m_open.return_value.writelines = result.extend
with patch('builtins.open', m_open, create=True):
self.nvm_def._generate_nvm_config_headers()
expected = (
'/*\n * vcc_nvm_struct.h - struct for NVM signals\n */\n\n'
'#ifndef VCC_NVM_STRUCT_H\n'
'#define VCC_NVM_STRUCT_H\n\n'
'#include "tl_basetypes.h"\n'
'#include "CVC_NVM_START.h"\n'
'struct DummySwc_NVM_LIST_8 {\n'
' UInt8 unused[56];\n'
'}; /* 0 bytes used of 56 */\n'
'#include "CVC_NVM_END.h"\n\n'
'#include "CVC_NVM_START.h"\n'
'struct DummySwc_NVM_LIST_16 {\n'
' UInt16 unused[240];\n'
'}; /* 0 bytes used of 480 */\n'
'#include "CVC_NVM_END.h"\n\n'
'#include "CVC_NVM_START.h"\n'
'struct DummySwc_NVM_LIST_32 {\n'
' UInt32 unused[975];\n'
'}; /* 0 bytes used of 3900 */\n'
'#include "CVC_NVM_END.h"\n\n'
'#include "CVC_NVM_P_START.h"\n'
'struct DummySwc_NVM_LIST_8_PER {\n'
' UInt8 unused[56];\n'
'}; /* 0 bytes used of 56 */\n'
'#include "CVC_NVM_P_END.h"\n\n'
'#include "CVC_NVM_P_START.h"\n'
'struct DummySwc_NVM_LIST_16_PER {\n'
' UInt16 unused[240];\n'
'}; /* 0 bytes used of 480 */\n'
'#include "CVC_NVM_P_END.h"\n\n'
'#include "CVC_NVM_P_START.h"\n'
'struct DummySwc_NVM_LIST_32_PER {\n'
' UInt32 unused[190];\n'
'}; /* 0 bytes used of 760 */\n'
'#include "CVC_NVM_P_END.h"\n\n'
'#include "PREDECL_START.h"\n'
'extern struct DummySwc_NVM_LIST_8 dummyswc_nvm_list_8;\n'
'extern struct DummySwc_NVM_LIST_16 dummyswc_nvm_list_16;\n'
'extern struct DummySwc_NVM_LIST_32 dummyswc_nvm_list_32;\n'
'extern struct DummySwc_NVM_LIST_8_PER dummyswc_nvm_list_8_per;\n'
'extern struct DummySwc_NVM_LIST_16_PER dummyswc_nvm_list_16_per;\n'
'extern struct DummySwc_NVM_LIST_32_PER dummyswc_nvm_list_32_per;\n'
'#include "PREDECL_END.h"\n\n'
'\n'
'\n#endif /* VCC_NVM_STRUCT_H */\n'
)
self.assertEqual("".join(result), expected)
@patch('builtins.open', new_callable=mock_open())
def test_generate_nvm_config_source_patch(self, mock_open_file):

@ -102,6 +102,7 @@ class TestCompositionYaml(unittest.TestCase):
self.zc_spec = copy.deepcopy(composition_yaml_setup.zc_spec)
self.nvm_def = MagicMock()
self.nvm_def.struct_member_prefix = "e_"
self.calibration_definitions = copy.deepcopy(composition_yaml_setup.calibration_definitions)