@h2loop/autosar 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/agents/autosar-integrator.md +45 -2
- package/commands/autosar-trace.md +2 -2
- package/package.json +4 -1
- package/plugins/index.js +55 -23
- package/python/ecuc_engine/__init__.py +1 -1
- package/python/ecuc_engine/add_signal.py +351 -0
- package/python/ecuc_engine/arxml_edit.py +387 -0
- package/python/ecuc_engine/audit.py +352 -50
- package/python/ecuc_engine/cli.py +125 -5
- package/python/ecuc_engine/codegen/__init__.py +233 -0
- package/python/ecuc_engine/codegen/binding.py +305 -0
- package/python/ecuc_engine/codegen/bindings/sip_standin.json +378 -0
- package/python/ecuc_engine/codegen/cdiff.py +395 -0
- package/python/ecuc_engine/codegen/cwriter.py +150 -0
- package/python/ecuc_engine/codegen/emit.py +463 -0
- package/python/ecuc_engine/codegen/handles.py +176 -0
- package/python/ecuc_engine/codegen/mcal/tc4d.json +60 -0
- package/python/ecuc_engine/codegen/model.py +264 -0
- package/python/ecuc_engine/codegen/modules/__init__.py +7 -0
- package/python/ecuc_engine/codegen/modules/com.py +145 -0
- package/python/ecuc_engine/codegen/modules/comstack.py +14 -0
- package/python/ecuc_engine/codegen/modules/dem.py +22 -0
- package/python/ecuc_engine/codegen/modules/mcal.py +170 -0
- package/python/ecuc_engine/codegen/modules/memstack.py +24 -0
- package/python/ecuc_engine/codegen/registry.py +208 -0
- package/python/ecuc_engine/codegen/schm_memmap.py +159 -0
- package/python/ecuc_engine/context.py +481 -0
- package/python/ecuc_engine/csrc.py +189 -0
- package/python/ecuc_engine/defs.py +30 -0
- package/python/ecuc_engine/ecucjson.py +75 -22
- package/python/ecuc_engine/find.py +164 -0
- package/python/ecuc_engine/gen.py +105 -0
- package/python/ecuc_engine/history.py +146 -0
- package/python/ecuc_engine/model.py +4 -1
- package/python/ecuc_engine/probe.py +229 -0
- package/python/ecuc_engine/rte_api.py +935 -0
- package/python/ecuc_engine/rules/__init__.py +5 -3
- package/python/ecuc_engine/rules/mem.py +84 -0
- package/python/ecuc_engine/rules/nvm.py +131 -0
- package/python/ecuc_engine/rules/pdur.py +87 -0
- package/python/ecuc_engine/rules/system.py +162 -0
- package/python/ecuc_engine/specids.py +163 -0
- package/python/ecuc_engine/sysmodel.py +152 -0
- package/python/ecuc_engine/validate.py +15 -2
- package/python/ecuc_engine/values.py +26 -0
- package/python/ecuc_engine/verify_rte.py +445 -0
- package/python/pyproject.toml +1 -1
- package/skills/autosar/SKILL.md +78 -6
- package/skills/autosar/references/codegen.md +58 -0
- package/skills/autosar/references/ecuc-model.md +4 -1
- package/skills/autosar/references/project-layout.md +10 -1
- package/skills/autosar/references/runnable-code.md +50 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"silicon": "tc4d",
|
|
3
|
+
"title": "Infineon AURIX TC4Dx: Port pad configuration (register level)",
|
|
4
|
+
"status": "skeleton",
|
|
5
|
+
"statusNote": "Only public AURIX TC4Dx user manual data is in this file. It is enough to compute the GPIO direction / level / MODE=0 (GPIO) pad settings of a pin whose port and pin number are given in the project pin map. It is NOT a replacement for the Infineon MCAL: alternate-function MODE numbers per pin and mode literal, pad driver/level defaults, access-protection handling and the MCAL's own Port_ConfigType layout come from Infineon's vendor param defs (VSMD) / BSWMD and MCAL static code, which are under NDA and not used here.",
|
|
6
|
+
"sources": [
|
|
7
|
+
{"id": "UM", "doc": "AURIX TC4Dx user manual v1.1 (2025-06-26), public", "file": "tc4d/docs/infineon-infineon-aurix-tc4dx-um-v1.1-public-en_us.pdf-usermanual-en.pdf"}
|
|
8
|
+
],
|
|
9
|
+
"registers": {
|
|
10
|
+
"GPIO": {"offset": "0x300", "stride": "0x10", "cite": "UM 47.4.2 Table 2432 (p. 8609) and 47.4.30 PADCFGp_GPIO (p. 8632-8633)"},
|
|
11
|
+
"DRVCFG": {"offset": "0x304", "stride": "0x10", "cite": "UM 47.4.2 Table 2432 (p. 8610) and 47.4.31 PADCFGp_DRVCFG (p. 8633-8637)"}
|
|
12
|
+
},
|
|
13
|
+
"fields": {
|
|
14
|
+
"DIR": {"reg": "DRVCFG", "lsb": 0, "width": 1, "cite": "UM p. 8634: 0B IN, 1B OUT"},
|
|
15
|
+
"OD": {"reg": "DRVCFG", "lsb": 1, "width": 1, "cite": "UM p. 8634: 0B push-pull, 1B open drain"},
|
|
16
|
+
"MODE": {"reg": "DRVCFG", "lsb": 4, "width": 4, "cite": "UM p. 8634: output: 0 = GPIO, n = alternate output function n; input: 0 tristate, 1 pull-down, 2 pull-up, 3 tristate"},
|
|
17
|
+
"PD": {"reg": "DRVCFG", "lsb": 8, "width": 3, "cite": "UM p. 8635: pad driver strength / edge"},
|
|
18
|
+
"PL": {"reg": "DRVCFG", "lsb": 12, "width": 3, "cite": "UM p. 8636: input level (AL / TTL)"},
|
|
19
|
+
"SET": {"reg": "GPIO", "lsb": 2, "width": 1, "cite": "UM p. 8633: write 1 drives the pin high (OUT is read-only)"},
|
|
20
|
+
"CLR": {"reg": "GPIO", "lsb": 3, "width": 1, "cite": "UM p. 8633: write 1 drives the pin low"}
|
|
21
|
+
},
|
|
22
|
+
"direction": {"PORT_PIN_IN": {"DIR": 0}, "PORT_PIN_OUT": {"DIR": 1}},
|
|
23
|
+
"level": {"PORT_PIN_LEVEL_HIGH": {"SET": 1}, "PORT_PIN_LEVEL_LOW": {"CLR": 1}},
|
|
24
|
+
"modes": {
|
|
25
|
+
"PORT_PIN_MODE_DIO": {"MODE": 0, "cite": "UM p. 8634: MODE 0 = GPIO (output) / tristate (input)"}
|
|
26
|
+
},
|
|
27
|
+
"modesNote": "Every other PortPinMode needs the alternate-function number of that pin (MODE = 1..15). Per-pin ALT numbers are listed in the TC4Dx data sheet pin tables, but the mapping from the vendor's PortPinMode literals to them is defined by Infineon's VSMD (NDA). Give them per pin in the project pin map ('modes': {'PORT_PIN_MODE_CAN': 2}).",
|
|
28
|
+
"ports": {
|
|
29
|
+
"P00": {"base": "0xF003A000", "pins": "0-15"},
|
|
30
|
+
"P01": {"base": "0xF003A400", "pins": "0-15"},
|
|
31
|
+
"P02": {"base": "0xF003A800", "pins": "0-15"},
|
|
32
|
+
"P03": {"base": "0xF003AC00", "pins": "0-15"},
|
|
33
|
+
"P04": {"base": "0xF003B000", "pins": "0-9"},
|
|
34
|
+
"P10": {"base": "0xF003C800", "pins": "0-11,13-15"},
|
|
35
|
+
"P13": {"base": "0xF003D400", "pins": "0-15"},
|
|
36
|
+
"P14": {"base": "0xF003D800", "pins": "0-15"},
|
|
37
|
+
"P15": {"base": "0xF003DC00", "pins": "0-8,10-15"},
|
|
38
|
+
"P16": {"base": "0xF003E000", "pins": "0-14"},
|
|
39
|
+
"P20": {"base": "0xF003F000", "pins": "0-3,6-14"},
|
|
40
|
+
"P21": {"base": "0xF003F400", "pins": "0-7"},
|
|
41
|
+
"P22": {"base": "0xF003F800", "pins": "0-11"},
|
|
42
|
+
"P23": {"base": "0xF003FC00", "pins": "0-7"},
|
|
43
|
+
"P25": {"base": "0xF0040400", "pins": "1"},
|
|
44
|
+
"P30": {"base": "0xF0041800", "pins": "0-15"},
|
|
45
|
+
"P31": {"base": "0xF0041C00", "pins": "6-7"},
|
|
46
|
+
"P32": {"base": "0xF0042000", "pins": "2,4-7"},
|
|
47
|
+
"P33": {"base": "0xF0042400", "pins": "0-15"},
|
|
48
|
+
"P34": {"base": "0xF0042800", "pins": "1-5"},
|
|
49
|
+
"P35": {"base": "0xF0042C00", "pins": "0-5"},
|
|
50
|
+
"P40": {"base": "0xF0044000", "pins": "0-14"}
|
|
51
|
+
},
|
|
52
|
+
"portsCite": "UM 47.8 Table 2448 'Registers address space - Pn' (p. 8650); available slices from Table 2446 'Port implementation in TC4DxAA-step' (p. 8645-8646)",
|
|
53
|
+
"needsVendorData": [
|
|
54
|
+
"Infineon MCAL Port/Dio vendor param defs (VSMD) and BSWMD: vendor PortPin parameters (driver strength, pad level, input select, alternate-function literals) and their config classes",
|
|
55
|
+
"the MCAL static code's Port_ConfigType / Dio config layout (what Port_Init actually consumes); without it only the register image below can be generated",
|
|
56
|
+
"per-pin alternate-function numbers for each PortPinMode literal (data sheet pin tables + the VSMD literal mapping)",
|
|
57
|
+
"write sequencing under the pad access protection (PADCFGp_ACCEN, ACCGRPy_*, PROTSE/Safety ENDINIT; UM 47.4.x) that the MCAL performs",
|
|
58
|
+
"PortPinId / DioChannelId encoding used by the Infineon MCAL (port*16+pin or otherwise); the project pin map gives port and pin explicitly instead"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""Generator view of the ECUC model: merged containers, effective config classes, refs, symbols.
|
|
2
|
+
|
|
3
|
+
Everything here is derived from the engine's loader (`model.Project`) and the param defs
|
|
4
|
+
(`defs.DefIndex`); nothing is module-specific. Container order is document order (files sorted by
|
|
5
|
+
path, elements in file order), so every table built from it is deterministic.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import Iterable, Optional
|
|
12
|
+
|
|
13
|
+
from .. import defs as D
|
|
14
|
+
from ..model import Elem, ParamVal, Project
|
|
15
|
+
from ..values import parse_float, parse_int
|
|
16
|
+
|
|
17
|
+
CLASS_RANK = {"PUBLISHED-INFORMATION": 0, "PRE-COMPILE": 1, "LINK": 2, "POST-BUILD": 3}
|
|
18
|
+
CLASS_FILE = {0: "Cfg.c", 1: "Cfg.c", 2: "Lcfg.c", 3: "PBcfg.c"}
|
|
19
|
+
DEFAULT_VARIANT = "VARIANT-PRE-COMPILE"
|
|
20
|
+
|
|
21
|
+
# AUTOSAR_CP_TR_ReleaseOverview R25-11, Table 1.2 "Mapping schema version to platform releases"
|
|
22
|
+
# (and Table 1.1 for the R4.x schemas): schema AUTOSAR_000NN -> internal release number.
|
|
23
|
+
SCHEMA_RELEASE = {
|
|
24
|
+
"00042": (4, 3, 0), "00043": (4, 3, 0), "00044": (4, 3, 1), "00045": (4, 3, 1), "00046": (4, 4, 0),
|
|
25
|
+
"00047": (4, 4, 0), "00048": (4, 5, 0), "00049": (4, 6, 0), "00050": (4, 7, 0), "00051": (4, 8, 0),
|
|
26
|
+
"00052": (4, 9, 0), "00053": (4, 10, 0), "00054": (4, 11, 0),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def snake(name: str) -> str:
|
|
31
|
+
"""CamelCase -> UPPER_SNAKE (NvMDevErrorDetect -> NVM_DEV_ERROR_DETECT).
|
|
32
|
+
|
|
33
|
+
The AUTOSAR glossary terms I-PDU / N-PDU / L-PDU stay one word (ComSupportedIPduGroups ->
|
|
34
|
+
COM_SUPPORTED_IPDU_GROUPS), as the specifications spell them in identifiers (IPDUM, ..._IPDU_...).
|
|
35
|
+
"""
|
|
36
|
+
s = re.sub(r"(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])", "_", name)
|
|
37
|
+
s = re.sub(r"(?<![A-Za-z])([INL])_(Pdu)", r"\1\2", s)
|
|
38
|
+
return re.sub(r"_+", "_", s).upper()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class Container:
|
|
43
|
+
"""One container value, merged across file splits (all fragments with the same AUTOSAR path)."""
|
|
44
|
+
path: str
|
|
45
|
+
name: str
|
|
46
|
+
defn: Optional[D.DefNode]
|
|
47
|
+
frags: list
|
|
48
|
+
module: "ModuleCfg"
|
|
49
|
+
parent: Optional["Container"] = None
|
|
50
|
+
children: list = field(default_factory=list)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def def_name(self) -> str:
|
|
54
|
+
return self.defn.name if self.defn else (self.frags[0].def_ref or "?").rsplit("/", 1)[-1]
|
|
55
|
+
|
|
56
|
+
def values(self, name: str) -> list:
|
|
57
|
+
return [pv for f in self.frags for pv in f.params if pv.name == name]
|
|
58
|
+
|
|
59
|
+
def value(self, name: str) -> Optional[ParamVal]:
|
|
60
|
+
v = self.values(name)
|
|
61
|
+
return v[0] if v else None
|
|
62
|
+
|
|
63
|
+
def text(self, name: str) -> Optional[str]:
|
|
64
|
+
v = self.value(name)
|
|
65
|
+
return v.value if v is not None else None
|
|
66
|
+
|
|
67
|
+
def sub(self, def_name: str) -> list:
|
|
68
|
+
return [c for c in self.children if c.def_name == def_name]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class ModuleCfg:
|
|
73
|
+
name: str # def short name (the Mip unless overridden)
|
|
74
|
+
elem: Elem
|
|
75
|
+
defn: Optional[D.DefNode]
|
|
76
|
+
variant: str
|
|
77
|
+
containers: list # all Containers, document order (pre-order)
|
|
78
|
+
top: list # top-level Containers
|
|
79
|
+
mip: str = ""
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def MIP(self) -> str:
|
|
83
|
+
return self.mip.upper()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class CgModel:
|
|
87
|
+
def __init__(self, project: Project, mip_overrides: Optional[dict] = None):
|
|
88
|
+
self.p = project
|
|
89
|
+
self.defs = project.defs
|
|
90
|
+
self.mip_overrides = mip_overrides or {}
|
|
91
|
+
self.modules: dict[str, ModuleCfg] = {}
|
|
92
|
+
self.by_path: dict[str, Container] = {}
|
|
93
|
+
self.order: dict[str, int] = {}
|
|
94
|
+
self._rev: Optional[dict] = None
|
|
95
|
+
self._sorted: dict = {}
|
|
96
|
+
for m in sorted(project.modules, key=lambda e: (e.file, e.line)):
|
|
97
|
+
self._add_module(m)
|
|
98
|
+
|
|
99
|
+
# ---------- construction ----------
|
|
100
|
+
def _def(self, def_ref: Optional[str]) -> Optional[D.DefNode]:
|
|
101
|
+
if not def_ref:
|
|
102
|
+
return None
|
|
103
|
+
d = self.defs.get(def_ref)
|
|
104
|
+
if d is not None:
|
|
105
|
+
return d
|
|
106
|
+
# vendor-relocated or other-release DEFINITION-REF: match by module-relative path
|
|
107
|
+
rel = self.defs.rel(def_ref)
|
|
108
|
+
for mp, md in self.defs.modules.items():
|
|
109
|
+
if rel.split("/", 1)[0] == md.name:
|
|
110
|
+
return self.defs.get(mp + rel[len(md.name):])
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
def _add_module(self, m: Elem):
|
|
114
|
+
md = self._def(m.def_ref)
|
|
115
|
+
name = md.name if md else (m.def_ref or m.name).rsplit("/", 1)[-1]
|
|
116
|
+
if name in self.modules: # split module configuration: merge into the first one
|
|
117
|
+
mc = self.modules[name]
|
|
118
|
+
else:
|
|
119
|
+
variant = m.node.child_text("IMPLEMENTATION-CONFIG-VARIANT") or DEFAULT_VARIANT
|
|
120
|
+
mc = ModuleCfg(name, m, md, variant, [], [], self.mip_overrides.get(name, name))
|
|
121
|
+
self.modules[name] = mc
|
|
122
|
+
for c in m.children:
|
|
123
|
+
self._add_container(mc, c, None)
|
|
124
|
+
|
|
125
|
+
def _add_container(self, mc: ModuleCfg, e: Elem, parent: Optional[Container]):
|
|
126
|
+
c = self.by_path.get(e.path)
|
|
127
|
+
if c is None:
|
|
128
|
+
c = Container(e.path, e.name, self._def(e.def_ref), [], mc, parent)
|
|
129
|
+
self.by_path[e.path] = c
|
|
130
|
+
self.order[e.path] = len(self.order)
|
|
131
|
+
mc.containers.append(c)
|
|
132
|
+
(parent.children if parent else mc.top).append(c)
|
|
133
|
+
c.frags.append(e)
|
|
134
|
+
for ch in e.children:
|
|
135
|
+
self._add_container(mc, ch, c)
|
|
136
|
+
|
|
137
|
+
# ---------- queries ----------
|
|
138
|
+
def by_path_index(self, c: Container) -> int:
|
|
139
|
+
return self.order.get(c.path, 0)
|
|
140
|
+
|
|
141
|
+
def module(self, name: str) -> Optional[ModuleCfg]:
|
|
142
|
+
return self.modules.get(name)
|
|
143
|
+
|
|
144
|
+
def instances(self, mc: ModuleCfg, def_name: str) -> list:
|
|
145
|
+
return [c for c in mc.containers if c.def_name == def_name]
|
|
146
|
+
|
|
147
|
+
def target(self, pv: Optional[ParamVal]) -> Optional[Container]:
|
|
148
|
+
if pv is None or not pv.value:
|
|
149
|
+
return None
|
|
150
|
+
return self.by_path.get(pv.value.strip())
|
|
151
|
+
|
|
152
|
+
def reverse(self) -> dict:
|
|
153
|
+
"""target path -> [(Container, ref name)] for every ECUC reference value, document order."""
|
|
154
|
+
if self._rev is None:
|
|
155
|
+
self._rev = {}
|
|
156
|
+
for mc in self.modules.values():
|
|
157
|
+
for c in mc.containers:
|
|
158
|
+
for f in c.frags:
|
|
159
|
+
for pv in f.params:
|
|
160
|
+
if pv.is_ref and pv.value:
|
|
161
|
+
self._rev.setdefault(pv.value.strip(), []).append((c, pv.name))
|
|
162
|
+
return self._rev
|
|
163
|
+
|
|
164
|
+
def referrers(self, target: Container, module: Optional[str] = None, ref: Optional[str] = None) -> list:
|
|
165
|
+
out = []
|
|
166
|
+
for c, rname in self.reverse().get(target.path, []):
|
|
167
|
+
if module is not None and c.module.name != module:
|
|
168
|
+
continue
|
|
169
|
+
if ref is not None and rname != ref:
|
|
170
|
+
continue
|
|
171
|
+
out.append(c)
|
|
172
|
+
return out
|
|
173
|
+
|
|
174
|
+
def param_def(self, c: Container, name: str) -> Optional[D.DefNode]:
|
|
175
|
+
if c.defn is None:
|
|
176
|
+
return None
|
|
177
|
+
for p in c.defn.params + c.defn.refs:
|
|
178
|
+
if p.rsplit("/", 1)[-1] == name:
|
|
179
|
+
return self.defs.get(p)
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
def eff_class(self, mc: ModuleCfg, d: Optional[D.DefNode]) -> int:
|
|
183
|
+
"""Effective config class rank of a param/ref (or container multiplicity) for the module's variant."""
|
|
184
|
+
if d is None:
|
|
185
|
+
return CLASS_RANK["PRE-COMPILE"]
|
|
186
|
+
cc = d.config_classes or d.mult_classes
|
|
187
|
+
if not cc:
|
|
188
|
+
return CLASS_RANK["PRE-COMPILE"]
|
|
189
|
+
cls = cc.get(mc.variant)
|
|
190
|
+
if cls is None: # variant not listed for this param: take the least dynamic class it supports
|
|
191
|
+
cls = min(cc.values(), key=lambda k: CLASS_RANK.get(k, 1))
|
|
192
|
+
return CLASS_RANK.get(cls, 1)
|
|
193
|
+
|
|
194
|
+
def symbolic_param(self, c: Container) -> Optional[D.DefNode]:
|
|
195
|
+
if c.defn is None:
|
|
196
|
+
return None
|
|
197
|
+
for p in c.defn.params:
|
|
198
|
+
d = self.defs.get(p)
|
|
199
|
+
if d is not None and d.symbolic_name:
|
|
200
|
+
return d
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
def symbol(self, c: Container) -> Optional[str]:
|
|
204
|
+
"""TPS_ECUC_02108: <Mip>Conf_<ContainerDefShortName>_<ContainerValueShortName>."""
|
|
205
|
+
if self.symbolic_param(c) is None:
|
|
206
|
+
return None
|
|
207
|
+
return f"{c.module.mip}Conf_{c.def_name}_{c.name}"
|
|
208
|
+
|
|
209
|
+
def symbol_value(self, c: Container) -> Optional[str]:
|
|
210
|
+
d = self.symbolic_param(c)
|
|
211
|
+
return c.text(d.name) if d is not None else None
|
|
212
|
+
|
|
213
|
+
def sorted_instances(self, mc: ModuleCfg, def_name: str) -> list:
|
|
214
|
+
"""Instances of a container def, ordered by their symbolic handle when they have one, else document order."""
|
|
215
|
+
key = (mc.name, def_name)
|
|
216
|
+
if key not in self._sorted:
|
|
217
|
+
insts = self.instances(mc, def_name)
|
|
218
|
+
if insts and self.symbolic_param(insts[0]) is not None:
|
|
219
|
+
def k(c):
|
|
220
|
+
v = parse_int(self.symbol_value(c) or "")
|
|
221
|
+
return (v is None, v if v is not None else 0)
|
|
222
|
+
insts = sorted(insts, key=k) # stable: ties stay in document order
|
|
223
|
+
self._sorted[key] = insts
|
|
224
|
+
return self._sorted[key]
|
|
225
|
+
|
|
226
|
+
def index_of(self, c: Container) -> int:
|
|
227
|
+
return self.sorted_instances(c.module, c.def_name).index(c)
|
|
228
|
+
|
|
229
|
+
def is_multi(self, c: Container) -> bool:
|
|
230
|
+
"""True if this container, or any ancestor, is an instance of a def with upper multiplicity > 1."""
|
|
231
|
+
x: Optional[Container] = c
|
|
232
|
+
while x is not None:
|
|
233
|
+
if x.defn is None or x.defn.upper != 1:
|
|
234
|
+
return True
|
|
235
|
+
x = x.parent
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
def release(self) -> tuple:
|
|
239
|
+
"""AUTOSAR release of the configuration, from the schema version of its files."""
|
|
240
|
+
for f in sorted(self.p.files):
|
|
241
|
+
head = self.p.files[f].data[:2048]
|
|
242
|
+
m = re.search(rb"AUTOSAR_(\d{5})\.xsd", head)
|
|
243
|
+
if m and m.group(1).decode() in SCHEMA_RELEASE:
|
|
244
|
+
return SCHEMA_RELEASE[m.group(1).decode()]
|
|
245
|
+
return (4, 11, 0)
|
|
246
|
+
|
|
247
|
+
def rel_files(self, mc: ModuleCfg) -> list:
|
|
248
|
+
files = sorted({f.file for c in mc.containers for f in c.frags} | {mc.elem.file})
|
|
249
|
+
return [self.p.rel(f).replace("\\", "/") for f in files]
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def num(v: Optional[str]) -> Optional[int]:
|
|
253
|
+
return parse_int(v) if v is not None else None
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def flt(v: Optional[str]) -> Optional[float]:
|
|
257
|
+
return parse_float(v) if v is not None else None
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def iter_param_defs(defs: D.DefIndex, d: D.DefNode) -> Iterable[D.DefNode]:
|
|
261
|
+
for p in d.params + d.refs:
|
|
262
|
+
x = defs.get(p)
|
|
263
|
+
if x is not None:
|
|
264
|
+
yield x
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Module generators. Every .py file here registers itself with @register("<ModuleDefName>")."""
|
|
2
|
+
import importlib
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
for _f in sorted(os.listdir(os.path.dirname(__file__))):
|
|
6
|
+
if _f.endswith(".py") and _f != "__init__.py":
|
|
7
|
+
importlib.import_module(f"{__name__}.{_f[:-3]}")
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Com: Com_Cfg.h / Com_PBcfg.c.
|
|
2
|
+
|
|
3
|
+
Def-driven (generic emitter): every ComGeneral switch, the TPS_ECUC_02108 symbols of ComSignal,
|
|
4
|
+
ComSignalGroup, ComGroupSignal, ComIPdu and ComIPduGroup (handle IDs), and, without a static-code
|
|
5
|
+
binding, one table per container def (I-PDUs, signals, signal groups, group signals with bit
|
|
6
|
+
position / size / endianness / type / transfer property / timeouts / update bits).
|
|
7
|
+
|
|
8
|
+
The static Com's own table layout (signal and I-PDU tables, buffers, Tx-mode ticks, deadline
|
|
9
|
+
ticks) comes from its binding (bindings/*.json); this module only adds what neither the defs nor
|
|
10
|
+
the binding path language can express:
|
|
11
|
+
* COM_DEV_ERROR_DETECT from ComConfigurationUseDet (ECUC_Com_00141 names that output macro);
|
|
12
|
+
* notification callouts, resolved for the binding through `call:` steps:
|
|
13
|
+
- `comUserCallback`: R25-11 Com users (SWS_Com 8.6.3.1, ECUC_Com_10034/10036): the ComUserModuleCnf
|
|
14
|
+
referenced by ComUserModule/ComUserModuleCnfRef, its ComUserSignal / ComUserSignalGroup with the
|
|
15
|
+
same system signal (group) as the ComSignal (Group), and the ComUserCallback of type COM_RX_ACK
|
|
16
|
+
(receiver) or COM_TX_ACK (sender). These callbacks take a ComUserCbkHandleId.
|
|
17
|
+
- `rteSignalCallback`: the per-signal RTE callback `Rte_COMCbk_<sn>()` (receiver) /
|
|
18
|
+
`Rte_COMCbkTAck_<sn>()` (sender), <sn> = ComSignal short name (SWS_Rte Figure 4.42), bound only
|
|
19
|
+
when the RTE's callback header (Rte_Cbk.h, from the project or the output dir) declares it with
|
|
20
|
+
a void(void) prototype; Com_PBcfg.c then includes that header;
|
|
21
|
+
* the headers named by ComGeneral/ComUserCbkHeaderFile (callback prototypes) in Com_PBcfg.c;
|
|
22
|
+
* notes for configuration a static Com may silently not implement.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import re
|
|
27
|
+
|
|
28
|
+
from ..registry import Generator, register
|
|
29
|
+
|
|
30
|
+
RX_ACK, TX_ACK = "COM_RX_ACK", "COM_TX_ACK"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@register("Com")
|
|
34
|
+
class Com(Generator):
|
|
35
|
+
std_header = "ComStack_Types.h"
|
|
36
|
+
|
|
37
|
+
def specific(self, mo):
|
|
38
|
+
gen = [c for c in self.mc.containers if c.def_name == "ComGeneral"]
|
|
39
|
+
g = gen[0] if gen else None
|
|
40
|
+
det = g.text("ComConfigurationUseDet") if g is not None else None
|
|
41
|
+
mo.group("Development error detection (ECUC_Com_00141: ComConfigurationUseDet -> COM_DEV_ERROR_DETECT)",
|
|
42
|
+
[("COM_DEV_ERROR_DETECT", "STD_ON" if (det or "").strip().lower() in ("true", "1") else "STD_OFF")])
|
|
43
|
+
if g is not None:
|
|
44
|
+
for pv in g.values("ComUserCbkHeaderFile"):
|
|
45
|
+
h = (pv.value or "").strip().strip('"<>')
|
|
46
|
+
if h:
|
|
47
|
+
mo.include(h)
|
|
48
|
+
self._dir = self._directions()
|
|
49
|
+
self._users = self._com_user_callbacks()
|
|
50
|
+
self._rte_text = self.ctx.header_text("Rte_Cbk.h")
|
|
51
|
+
self._limits(mo)
|
|
52
|
+
|
|
53
|
+
# ---------- call: resolvers for the binding ----------
|
|
54
|
+
|
|
55
|
+
def path_steps(self, mo) -> dict:
|
|
56
|
+
def com_user(c, ev):
|
|
57
|
+
hit = self._users.get((c.path, self._ack(c)))
|
|
58
|
+
if hit is None:
|
|
59
|
+
return None
|
|
60
|
+
name, header = hit
|
|
61
|
+
if header:
|
|
62
|
+
mo.include(header)
|
|
63
|
+
return name
|
|
64
|
+
|
|
65
|
+
def rte_signal(c, ev):
|
|
66
|
+
if c.def_name not in ("ComSignal", "ComSignalGroup") or not self._rte_text:
|
|
67
|
+
return None
|
|
68
|
+
name = ("Rte_COMCbk_" if self._ack(c) == RX_ACK else "Rte_COMCbkTAck_") + c.name
|
|
69
|
+
if not re.search(rf"\b{re.escape(name)}\s*\(\s*void\s*\)", self._rte_text):
|
|
70
|
+
if (c.path, self._ack(c)) in self._users:
|
|
71
|
+
mo.notes.append(f"{c.path}: has a Com user callback ({self._users[(c.path, self._ack(c))][0]}), "
|
|
72
|
+
"which takes a ComUserCbkHandleId; the static code's notification type is "
|
|
73
|
+
"void(void), so it is not bound")
|
|
74
|
+
return None
|
|
75
|
+
mo.include("Rte_Cbk.h")
|
|
76
|
+
return name
|
|
77
|
+
|
|
78
|
+
return {"comUserCallback": com_user, "rteSignalCallback": rte_signal}
|
|
79
|
+
|
|
80
|
+
# ---------- helpers ----------
|
|
81
|
+
|
|
82
|
+
def _directions(self) -> dict:
|
|
83
|
+
"""ComSignal / ComSignalGroup path -> ComIPduDirection of the I-PDU that carries it."""
|
|
84
|
+
out = {}
|
|
85
|
+
for ip in self.cg.instances(self.mc, "ComIPdu"):
|
|
86
|
+
d = (ip.text("ComIPduDirection") or "").strip()
|
|
87
|
+
for r in ("ComIPduSignalRef", "ComIPduSignalGroupRef"):
|
|
88
|
+
for pv in ip.values(r):
|
|
89
|
+
if pv.value:
|
|
90
|
+
out.setdefault(pv.value.strip(), d)
|
|
91
|
+
return out
|
|
92
|
+
|
|
93
|
+
def _ack(self, c) -> str:
|
|
94
|
+
return TX_ACK if self._dir.get(c.path) == "SEND" else RX_ACK
|
|
95
|
+
|
|
96
|
+
def _com_user_callbacks(self) -> dict:
|
|
97
|
+
"""(ComSignal/ComSignalGroup path, callback type) -> (ComUserCallbackName, ComUserHeaderInclude)."""
|
|
98
|
+
by_sys: dict = {}
|
|
99
|
+
for c in self.mc.containers:
|
|
100
|
+
for p in ("ComSystemTemplateSystemSignalRef", "ComSystemTemplateSignalGroupRef"):
|
|
101
|
+
v = c.text(p)
|
|
102
|
+
if v and c.def_name in ("ComSignal", "ComSignalGroup"):
|
|
103
|
+
by_sys.setdefault(v.strip(), c)
|
|
104
|
+
out: dict = {}
|
|
105
|
+
for um in self.cg.instances(self.mc, "ComUserModule"):
|
|
106
|
+
cnf = self.cg.target(um.value("ComUserModuleCnfRef"))
|
|
107
|
+
if cnf is None:
|
|
108
|
+
continue
|
|
109
|
+
header = (cnf.text("ComUserHeaderInclude") or "").strip().strip('"<>') or None
|
|
110
|
+
for u in cnf.children:
|
|
111
|
+
sysref = u.text("ComUserSystemTemplateSystemSignalRef") or u.text("ComUserSystemTemplateSignalGroupRef")
|
|
112
|
+
sig = by_sys.get((sysref or "").strip())
|
|
113
|
+
if sig is None:
|
|
114
|
+
continue
|
|
115
|
+
for pv in u.values("ComUserCallbackRef"):
|
|
116
|
+
cb = self.cg.target(pv)
|
|
117
|
+
if cb is None:
|
|
118
|
+
continue
|
|
119
|
+
kind = (cb.text("ComUserCallbackType") or "").strip()
|
|
120
|
+
name = (cb.text("ComUserCallbackName") or "").strip()
|
|
121
|
+
if name:
|
|
122
|
+
out.setdefault((sig.path, kind), (name, header))
|
|
123
|
+
return out
|
|
124
|
+
|
|
125
|
+
def _limits(self, mo):
|
|
126
|
+
"""Configuration that is valid ECUC but that a static Com may not implement: say so, never drop it silently."""
|
|
127
|
+
if mo.binding is None:
|
|
128
|
+
return
|
|
129
|
+
cnt = lambda pred: sum(1 for c in self.mc.containers if pred(c)) # noqa: E731
|
|
130
|
+
notes = []
|
|
131
|
+
n = cnt(lambda c: c.def_name == "ComIPdu" and (c.text("ComIPduSignalProcessing") or "").strip() == "DEFERRED")
|
|
132
|
+
if n:
|
|
133
|
+
notes.append(f"{n} I-PDU(s) use ComIPduSignalProcessing=DEFERRED; the binding has no field for it "
|
|
134
|
+
"(the static code decides when Rx notifications run)")
|
|
135
|
+
n = cnt(lambda c: c.def_name == "ComSignal" and (c.text("ComRxDataTimeoutAction") or "NONE").strip() != "NONE")
|
|
136
|
+
if n:
|
|
137
|
+
notes.append(f"{n} signal(s) configure ComRxDataTimeoutAction; the binding carries only the I-PDU "
|
|
138
|
+
"deadline (TimeoutTicks), the action is up to the static code")
|
|
139
|
+
n = cnt(lambda c: c.def_name == "ComTxMode" and int(float(c.text("ComTxModeNumberOfRepetitions") or 0)) > 0)
|
|
140
|
+
if n:
|
|
141
|
+
notes.append(f"{n} Tx mode(s) configure ComTxModeNumberOfRepetitions > 0; the binding has no repetition field")
|
|
142
|
+
n = cnt(lambda c: c.def_name == "ComTxIPdu" and float(c.text("ComMinimumDelayTime") or 0) > 0)
|
|
143
|
+
if n:
|
|
144
|
+
notes.append(f"{n} Tx I-PDU(s) configure ComMinimumDelayTime > 0; the binding has no MDT field")
|
|
145
|
+
mo.notes += [f"{(mo.binding or {}).get('_binding')}: {x}" for x in notes]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""PduR and CanIf. Handle IDs, routing and PDU tables are def-driven; the neighbour handles
|
|
2
|
+
(PduR -> CanIf/Com, CanIf -> PduR) are found through the shared EcucPduCollection/Pdu
|
|
3
|
+
(TPS_ECUC 3.4.1.3) by the binding path language (`peer:`). ComStack_Types.h is the base header."""
|
|
4
|
+
from ..registry import Generator, register
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@register("PduR")
|
|
8
|
+
class PduR(Generator):
|
|
9
|
+
std_header = "ComStack_Types.h"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@register("CanIf")
|
|
13
|
+
class CanIf(Generator):
|
|
14
|
+
std_header = "ComStack_Types.h"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Dem: event IDs and the other symbolic IDs are generic (TPS_ECUC_02108). Module-specific:
|
|
2
|
+
* the DTC value of every DemDTC as DEM_DTC_<NAME> (DemDtcValue is not a symbolicNameValue param,
|
|
3
|
+
so TPS_ECUC_02108 does not name it; this generator's convention, <MIP>_<SN> per SWS_BSW_00124);
|
|
4
|
+
* Dem_MainFunction, scheduled with DemGeneral/DemTaskTime (ECUC_Dem_00715)."""
|
|
5
|
+
from .. import cwriter as W
|
|
6
|
+
from ..model import snake
|
|
7
|
+
from ..registry import Generator, register
|
|
8
|
+
from ...values import parse_int
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@register("Dem")
|
|
12
|
+
class Dem(Generator):
|
|
13
|
+
def specific(self, mo):
|
|
14
|
+
lines = []
|
|
15
|
+
for c in self.cg.instances(self.mc, "DemDTC"):
|
|
16
|
+
v = parse_int(c.text("DemDtcValue") or "")
|
|
17
|
+
if v is not None:
|
|
18
|
+
lines.append((f"DEM_DTC_{snake(c.name)}", W.c_uint(v, hexa=True), "DemDtcValue"))
|
|
19
|
+
mo.group("DTC values (DemDTC/DemDtcValue)", lines)
|
|
20
|
+
gen = [c for c in self.mc.containers if c.def_name == "DemGeneral"]
|
|
21
|
+
if gen and gen[0].text("DemTaskTime") is not None:
|
|
22
|
+
mo.main_functions = ["Dem_MainFunction"]
|