@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,445 @@
|
|
|
1
|
+
"""Verify Rte_* calls in C against the SW-C's expected RTE API (rte_api), per runnable.
|
|
2
|
+
|
|
3
|
+
Each call (found with the audit's call scanner) is mapped to its enclosing function, then to every runnable whose
|
|
4
|
+
entry point (the RUNNABLE-ENTITY SYMBOL) reaches that function through the local call graph, and classified:
|
|
5
|
+
OK the runnable has the access point for the API
|
|
6
|
+
NOT-ALLOWED the API exists for the SW-C (port/element/IRV/... declared) but this runnable has no access point
|
|
7
|
+
INVENTED no such API for the SW-C (unknown port, element, runnable, wrong direction/semantics, ...)
|
|
8
|
+
For NOT-ALLOWED and INVENTED it proposes the exact ARXML change as arxml_edit ops (checked with a dry run).
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
import subprocess
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
from . import paths
|
|
18
|
+
from .csrc import CIndex
|
|
19
|
+
from .rte_api import (COLLECTION, PER_RUNNABLE, ApiEntry, ArIndex, SwcModel, build_all, find_swc, needed_role,
|
|
20
|
+
split_name)
|
|
21
|
+
|
|
22
|
+
PROVIDE_KINDS = {"Write", "Send", "Invalidate", "Feedback", "IWrite", "IWriteRef", "IInvalidate", "Switch", "SwitchAck",
|
|
23
|
+
"Trigger"}
|
|
24
|
+
REQUIRE_KINDS = {"Read", "Receive", "DRead", "IsUpdated", "IRead", "IReadRef", "IStatus", "Call", "Result", "Prm", "Mode"}
|
|
25
|
+
SR_KINDS = {"Write", "Send", "Invalidate", "Feedback", "IWrite", "IWriteRef", "IInvalidate", "Read", "Receive", "DRead",
|
|
26
|
+
"IsUpdated", "IRead", "IReadRef", "IStatus"}
|
|
27
|
+
KIND_ROLE = {"Write": "dataSendPoint", "Send": "dataSendPoint", "Read": "dataReceivePointByArgument",
|
|
28
|
+
"Receive": "dataReceivePointByArgument", "DRead": "dataReceivePointByValue", "IRead": "dataReadAccess",
|
|
29
|
+
"IReadRef": "dataReadAccess", "IWrite": "dataWriteAccess", "IWriteRef": "dataWriteAccess",
|
|
30
|
+
"Call": "serverCallPoint", "Mode": "modeAccessPoint", "Switch": "modeSwitchPoint", "Prm": "parameterAccess",
|
|
31
|
+
"IrvRead": "readLocalVariable", "IrvIRead": "readLocalVariable", "IrvIReadRef": "readLocalVariable",
|
|
32
|
+
"IrvWrite": "writtenLocalVariable", "IrvIWrite": "writtenLocalVariable",
|
|
33
|
+
"IrvIWriteRef": "writtenLocalVariable"}
|
|
34
|
+
PREFIX = {"dataSendPoint": "SEND", "dataReceivePointByArgument": "REC", "dataReceivePointByValue": "RBV",
|
|
35
|
+
"dataReadAccess": "IREAD", "dataWriteAccess": "IWRITE", "serverCallPoint": "SCP",
|
|
36
|
+
"modeAccessPoint": "MAP", "modeSwitchPoint": "MSP", "parameterAccess": "PA", "readLocalVariable": "IRVR",
|
|
37
|
+
"writtenLocalVariable": "IRVW"}
|
|
38
|
+
# Not SW-C APIs: RTE lifecycle / BSW-facing / callbacks (SWS_RTE 5.8, 5.9) — left to the integration code
|
|
39
|
+
LIFECYCLE = re.compile(r"^Rte_(Start|Stop|Init|StartTiming|PartitionTerminated|PartitionRestarting|RestartPartition|"
|
|
40
|
+
r"COMCbk|ComCbk|SetMetaDataItem|GetMetaDataItem|GetMetaDataLength|MainFunction|"
|
|
41
|
+
r"ComDataMainFunction|Ports|NPorts|Port)\w*$")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------- git helpers
|
|
45
|
+
def changed_lines(root: str, ref: str) -> tuple:
|
|
46
|
+
"""{abs file: set(lines) | None (=whole file)} of C changes in the work tree since ``ref``; error text."""
|
|
47
|
+
try:
|
|
48
|
+
r = subprocess.run(["git", "-C", root, "diff", "-U0", "--no-color", "--no-ext-diff", ref, "--"],
|
|
49
|
+
capture_output=True, text=True, timeout=120)
|
|
50
|
+
except (OSError, subprocess.TimeoutExpired) as e:
|
|
51
|
+
return {}, f"git diff failed: {e}"
|
|
52
|
+
if r.returncode != 0:
|
|
53
|
+
return {}, f"git diff {ref} failed: {r.stderr.strip()[:300]}"
|
|
54
|
+
top = subprocess.run(["git", "-C", root, "rev-parse", "--show-toplevel"], capture_output=True, text=True).stdout.strip() or root
|
|
55
|
+
out: dict = {}
|
|
56
|
+
cur = None
|
|
57
|
+
for ln in r.stdout.splitlines():
|
|
58
|
+
if ln.startswith("+++ "):
|
|
59
|
+
p = ln[4:].strip()
|
|
60
|
+
cur = None if p == "/dev/null" else os.path.join(top, p[2:] if p.startswith("b/") else p)
|
|
61
|
+
if cur and paths.is_c(cur):
|
|
62
|
+
out.setdefault(cur, set())
|
|
63
|
+
else:
|
|
64
|
+
cur = None
|
|
65
|
+
elif ln.startswith("@@") and cur:
|
|
66
|
+
m = re.search(r"\+(\d+)(?:,(\d+))?", ln)
|
|
67
|
+
if m:
|
|
68
|
+
a, n = int(m.group(1)), int(m.group(2) or 1)
|
|
69
|
+
out[cur].update(range(a, a + n))
|
|
70
|
+
u = subprocess.run(["git", "-C", root, "ls-files", "--others", "--exclude-standard"], capture_output=True, text=True)
|
|
71
|
+
for p in u.stdout.splitlines():
|
|
72
|
+
if paths.is_c(p):
|
|
73
|
+
out[os.path.join(root, p)] = None
|
|
74
|
+
return out, None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------- suggestions
|
|
78
|
+
class Suggest:
|
|
79
|
+
def __init__(self, ix: ArIndex, models: list):
|
|
80
|
+
self.ix, self.models = ix, models
|
|
81
|
+
|
|
82
|
+
def _type_ref(self, element: Optional[str]) -> tuple:
|
|
83
|
+
"""A type for a new data element: the type of a same-named element elsewhere, else the platform uint8."""
|
|
84
|
+
if element:
|
|
85
|
+
for n in self.ix.of_tag("VARIABLE-DATA-PROTOTYPE", "PARAMETER-DATA-PROTOTYPE"):
|
|
86
|
+
if n.short_name == element and n.child_text("TYPE-TREF"):
|
|
87
|
+
return n.child_text("TYPE-TREF"), False
|
|
88
|
+
for n in self.ix.of_tag("IMPLEMENTATION-DATA-TYPE"):
|
|
89
|
+
if n.short_name == "uint8":
|
|
90
|
+
return self.ix.path(n), True
|
|
91
|
+
idts = self.ix.of_tag("IMPLEMENTATION-DATA-TYPE")
|
|
92
|
+
return (self.ix.path(idts[0]) if idts else "/AUTOSAR_Platform/ImplementationDataTypes/uint8"), True
|
|
93
|
+
|
|
94
|
+
def _unique(self, parent: str, base: str) -> str:
|
|
95
|
+
name, i = base, 2
|
|
96
|
+
while self.ix.get(f"{parent}/{name}") is not None:
|
|
97
|
+
name, i = f"{base}_{i}", i + 1
|
|
98
|
+
return name
|
|
99
|
+
|
|
100
|
+
def access_op(self, m: SwcModel, runnable: str, role: str, port_path: Optional[str], target: str,
|
|
101
|
+
port_name: str = "", element: str = "") -> dict:
|
|
102
|
+
r = m.runnable(runnable)
|
|
103
|
+
coll = COLLECTION.get(role)
|
|
104
|
+
if role in ("readLocalVariable", "writtenLocalVariable"):
|
|
105
|
+
values = {"ACCESSED-VARIABLE": {"LOCAL-VARIABLE-REF": "->" + target}}
|
|
106
|
+
tag = "VARIABLE-ACCESS"
|
|
107
|
+
elif role == "serverCallPoint":
|
|
108
|
+
tag, values = "SYNCHRONOUS-SERVER-CALL-POINT", {"OPERATION-IREF": {"CONTEXT-R-PORT-REF": "->" + port_path,
|
|
109
|
+
"TARGET-REQUIRED-OPERATION-REF": "->" + target}}
|
|
110
|
+
elif role == "modeAccessPoint":
|
|
111
|
+
prov = any(p["path"] == port_path and p["direction"] == "P" for p in m.ports)
|
|
112
|
+
side = "P" if prov else "R"
|
|
113
|
+
tag, values = "MODE-ACCESS-POINT", {"MODE-GROUP-IREF": {f"{side}-MODE-GROUP-IN-ATOMIC-SWC-INSTANCE-REF": {
|
|
114
|
+
f"CONTEXT-{side}-PORT-REF": "->" + port_path, "TARGET-MODE-GROUP-REF": "->" + target}}}
|
|
115
|
+
elif role == "modeSwitchPoint":
|
|
116
|
+
tag, values = "MODE-SWITCH-POINT", {"MODE-GROUP-IREF": {"CONTEXT-P-PORT-REF": "->" + port_path,
|
|
117
|
+
"TARGET-MODE-GROUP-REF": "->" + target}}
|
|
118
|
+
elif role == "parameterAccess":
|
|
119
|
+
tag = "PARAMETER-ACCESS"
|
|
120
|
+
values = {"ACCESSED-PARAMETER": ({"AUTOSAR-PARAMETER-IREF": {"PORT-PROTOTYPE-REF": "->" + port_path,
|
|
121
|
+
"TARGET-DATA-PROTOTYPE-REF": "->" + target}}
|
|
122
|
+
if port_path else {"LOCAL-PARAMETER-REF": "->" + target})}
|
|
123
|
+
else:
|
|
124
|
+
tag, values = "VARIABLE-ACCESS", {"ACCESSED-VARIABLE": {"AUTOSAR-VARIABLE-IREF": {
|
|
125
|
+
"PORT-PROTOTYPE-REF": "->" + port_path, "TARGET-DATA-PROTOTYPE-REF": "->" + target}}}
|
|
126
|
+
op = {"op": "add", "parent_path": r.path, "element": tag, "collection": coll, "values": values}
|
|
127
|
+
if tag != "MODE-ACCESS-POINT": # ModeAccessPoint is not identifiable
|
|
128
|
+
base = "_".join(x for x in (PREFIX.get(role, "AP"), port_name, element or target.rsplit("/", 1)[-1]) if x)
|
|
129
|
+
op["short_name"] = self._unique(r.path, base)
|
|
130
|
+
return op
|
|
131
|
+
|
|
132
|
+
def for_entry(self, m: SwcModel, e: ApiEntry, runnable: str) -> tuple:
|
|
133
|
+
role = needed_role(e)
|
|
134
|
+
if e.api in ("Enter", "Exit"):
|
|
135
|
+
r = m.runnable(runnable)
|
|
136
|
+
return [{"op": "add", "parent_path": r.path, "element": "CAN-ENTER-EXCLUSIVE-AREA-REF",
|
|
137
|
+
"collection": "CAN-ENTER-EXCLUSIVE-AREA-REFS", "values": "->" + e.target}], None
|
|
138
|
+
if role not in COLLECTION:
|
|
139
|
+
return [], f"add a {role} for {e.target} to runnable {runnable}"
|
|
140
|
+
hint = None
|
|
141
|
+
if e.runnable and e.runnable != runnable: # Rte_IWrite_<other re>_...: per-runnable APIs name the caller
|
|
142
|
+
fixed = e.name.replace(f"_{e.runnable}_", f"_{runnable}_", 1)
|
|
143
|
+
hint = f"{e.name} belongs to runnable {e.runnable}; from {runnable} call {fixed} (after adding the access point)"
|
|
144
|
+
return [self.access_op(m, runnable, role, e.port_path, e.target, e.port or "", e.element or "")], hint
|
|
145
|
+
|
|
146
|
+
def interfaces_with(self, kind: str, element: str) -> list:
|
|
147
|
+
tag, coll = {"sr": ("SENDER-RECEIVER-INTERFACE", "DATA-ELEMENTS"), "cs": ("CLIENT-SERVER-INTERFACE", "OPERATIONS"),
|
|
148
|
+
"mode": ("MODE-SWITCH-INTERFACE", "MODE-GROUP"), "prm": ("PARAMETER-INTERFACE", "PARAMETERS")}[kind]
|
|
149
|
+
out = []
|
|
150
|
+
for itf in self.ix.of_tag(tag):
|
|
151
|
+
holder = itf.child(coll)
|
|
152
|
+
kids = (holder.children if holder is not None and holder.tag == coll and coll != "MODE-GROUP"
|
|
153
|
+
else [holder] if holder is not None else [])
|
|
154
|
+
if any(k.short_name == element for k in kids):
|
|
155
|
+
out.append(self.ix.path(itf))
|
|
156
|
+
return out
|
|
157
|
+
|
|
158
|
+
def users_of(self, itf: str) -> list:
|
|
159
|
+
return sorted({f"{m.name}.{p['name']}" for m in self.models for p in m.ports if p["interface"] == itf})
|
|
160
|
+
|
|
161
|
+
def for_invented(self, m: SwcModel, name: str, runnable: Optional[str]) -> tuple:
|
|
162
|
+
"""-> (ops, hint). ops create the missing port / element / IRV and the access point."""
|
|
163
|
+
kind, rest = split_name(name)
|
|
164
|
+
if kind is None:
|
|
165
|
+
return [], "not an RTE API name; use one of the allowed APIs listed by autosar_build_context"
|
|
166
|
+
run = m.runnable(runnable) if runnable else None
|
|
167
|
+
if kind in PER_RUNNABLE:
|
|
168
|
+
rn = next((r.name for r in sorted(m.runnables, key=lambda r: -len(r.name)) if rest.startswith(r.name + "_")), None)
|
|
169
|
+
if rn is None:
|
|
170
|
+
return [], f"Rte_{kind} must name the calling runnable: Rte_{kind}_{run.name if run else '<re>'}_..."
|
|
171
|
+
if run is not None and rn != run.name:
|
|
172
|
+
fixed = f"Rte_{kind}_{run.name}_{rest[len(rn) + 1:]}"
|
|
173
|
+
return [], f"names runnable {rn} but is called from {run.name}; per-runnable APIs use the caller: {fixed}"
|
|
174
|
+
rest = rest[len(rn) + 1:]
|
|
175
|
+
if run is None:
|
|
176
|
+
return [], "call site is not reachable from a runnable of this SW-C; cannot place an access point"
|
|
177
|
+
if kind.startswith("Irv"):
|
|
178
|
+
if m.behavior is None:
|
|
179
|
+
return [], f"{m.name} has no SWC-INTERNAL-BEHAVIOR"
|
|
180
|
+
implicit = kind.startswith("IrvI")
|
|
181
|
+
coll = "IMPLICIT-INTER-RUNNABLE-VARIABLES" if implicit else "EXPLICIT-INTER-RUNNABLE-VARIABLES"
|
|
182
|
+
tref, assumed = self._type_ref(rest)
|
|
183
|
+
irv = f"{m.behavior}/{rest}"
|
|
184
|
+
ops = [{"op": "add", "parent_path": m.behavior, "element": "VARIABLE-DATA-PROTOTYPE", "collection": coll,
|
|
185
|
+
"short_name": rest, "values": {"TYPE-TREF": "->" + tref}},
|
|
186
|
+
self.access_op(m, run.name, KIND_ROLE[kind], None, irv, "", rest)]
|
|
187
|
+
return ops, ("type assumed: " + tref + "; adjust TYPE-TREF" if assumed else None)
|
|
188
|
+
if kind in ("Enter", "Exit"):
|
|
189
|
+
ea = f"{m.behavior}/{rest}"
|
|
190
|
+
return [{"op": "add", "parent_path": m.behavior, "element": "EXCLUSIVE-AREA", "collection": "EXCLUSIVE-AREAS",
|
|
191
|
+
"short_name": rest},
|
|
192
|
+
{"op": "add", "parent_path": run.path, "element": "CAN-ENTER-EXCLUSIVE-AREA-REF",
|
|
193
|
+
"collection": "CAN-ENTER-EXCLUSIVE-AREA-REFS", "values": "->" + ea}], None
|
|
194
|
+
if kind in ("Pim", "CData", "IrTrigger", "Trigger", "SwitchAck", "Feedback", "Invalidate", "IInvalidate",
|
|
195
|
+
"IsUpdated", "IStatus", "Result"):
|
|
196
|
+
return [], explain(m, name)
|
|
197
|
+
role = KIND_ROLE.get(kind)
|
|
198
|
+
ports = [p for p in m.ports if rest.startswith(p["name"] + "_")]
|
|
199
|
+
port = max(ports, key=lambda p: len(p["name"])) if ports else None
|
|
200
|
+
if port is not None:
|
|
201
|
+
el = rest[len(port["name"]) + 1:]
|
|
202
|
+
elem = next((e for e in port["elements"] if e["name"] == el), None)
|
|
203
|
+
want_dir = "P" if kind in PROVIDE_KINDS else "R"
|
|
204
|
+
if elem is not None: # element exists: direction or semantics do not fit
|
|
205
|
+
alt = {"Write": "Read", "Read": "Write", "Send": "Receive", "Receive": "Send", "IWrite": "IRead",
|
|
206
|
+
"IRead": "IWrite"}.get(kind)
|
|
207
|
+
if want_dir not in port["direction"]:
|
|
208
|
+
return [], (f"port {port['name']} is a {port['direction']}-port; Rte_{kind} needs a "
|
|
209
|
+
f"{want_dir}-port" + (f" (on this port use Rte_{alt}_{port['name']}_{el})" if alt else ""))
|
|
210
|
+
sem = elem.get("semantics")
|
|
211
|
+
if sem == "data" and kind in ("Send", "Receive"):
|
|
212
|
+
return [], f"{el} has data semantics: use Rte_{'Write' if kind == 'Send' else 'Read'}_{port['name']}_{el}"
|
|
213
|
+
if sem == "event" and kind in ("Write", "Read", "IWrite", "IRead", "DRead"):
|
|
214
|
+
return [], f"{el} is queued (event semantics): use Rte_{'Send' if 'Write' in kind else 'Receive'}_{port['name']}_{el}"
|
|
215
|
+
return [], explain(m, name)
|
|
216
|
+
ik = port["interfaceKind"]
|
|
217
|
+
want = "sr" if kind in SR_KINDS else "cs" if kind == "Call" else "mode" if kind in ("Mode", "Switch") else "prm"
|
|
218
|
+
have = {"SENDER-RECEIVER-INTERFACE": "sr", "NV-DATA-INTERFACE": "sr", "CLIENT-SERVER-INTERFACE": "cs",
|
|
219
|
+
"MODE-SWITCH-INTERFACE": "mode", "PARAMETER-INTERFACE": "prm"}.get(ik)
|
|
220
|
+
if want != have or want not in ("sr", "prm"):
|
|
221
|
+
return [], f"port {port['name']} ({ik}) has no element '{el}'; Rte_{kind} needs a {want} element"
|
|
222
|
+
ops = self.new_element(port["interface"], want, el, kind)
|
|
223
|
+
target = f"{port['interface']}/{el}"
|
|
224
|
+
ops.append(self.access_op(m, run.name, role, port["path"], target, port["name"], el))
|
|
225
|
+
users = self.users_of(port["interface"])
|
|
226
|
+
hint = (f"adds element {el} to interface {port['interface']}, which is also used by: "
|
|
227
|
+
f"{', '.join(u for u in users if not u.startswith(m.name + '.')) or 'no other port'}")
|
|
228
|
+
return ops, hint
|
|
229
|
+
# unknown port: Rte_<k>_<port>_<element>, split at the last underscore
|
|
230
|
+
if "_" not in rest:
|
|
231
|
+
return [], f"Rte_{kind}_<port>_<element> expected, got '{rest}'"
|
|
232
|
+
pname, el = rest.rsplit("_", 1)
|
|
233
|
+
want = "sr" if kind in SR_KINDS else "cs" if kind == "Call" else "mode" if kind in ("Mode", "Switch") else "prm"
|
|
234
|
+
if role is None:
|
|
235
|
+
return [], explain(m, name)
|
|
236
|
+
cands = self.interfaces_with(want, el)
|
|
237
|
+
ops = []
|
|
238
|
+
notes = []
|
|
239
|
+
if cands:
|
|
240
|
+
itf = sorted(cands, key=lambda i: (-len(self.users_of(i)), i))[0]
|
|
241
|
+
notes.append(f"reuses existing interface {itf} (has {el}; used by {', '.join(self.users_of(itf)) or 'no port'})")
|
|
242
|
+
elif want in ("sr", "prm"):
|
|
243
|
+
pkg = self.interface_package(m)
|
|
244
|
+
iname = self._unique(pkg, f"{pname}_If")
|
|
245
|
+
itf = f"{pkg}/{iname}"
|
|
246
|
+
tref, assumed = self._type_ref(el)
|
|
247
|
+
elem = self.element_values(want, el, tref, kind)
|
|
248
|
+
ops.append({"op": "add", "parent_path": pkg, "element": "SENDER-RECEIVER-INTERFACE" if want == "sr"
|
|
249
|
+
else "PARAMETER-INTERFACE", "short_name": iname,
|
|
250
|
+
"values": ({"IS-SERVICE": False, "DATA-ELEMENTS": {"VARIABLE-DATA-PROTOTYPE": elem}} if want == "sr"
|
|
251
|
+
else {"PARAMETERS": {"PARAMETER-DATA-PROTOTYPE": elem}})})
|
|
252
|
+
if assumed:
|
|
253
|
+
notes.append(f"new interface {itf}: element type assumed {tref}; adjust TYPE-TREF")
|
|
254
|
+
else:
|
|
255
|
+
return [], f"no {want} interface with element '{el}' exists; create the interface first"
|
|
256
|
+
ptag = "P-PORT-PROTOTYPE" if kind in PROVIDE_KINDS else "R-PORT-PROTOTYPE"
|
|
257
|
+
ops.append({"op": "add", "parent_path": m.path, "element": ptag, "collection": "PORTS", "short_name": pname,
|
|
258
|
+
"values": {("PROVIDED-INTERFACE-TREF" if ptag.startswith("P") else "REQUIRED-INTERFACE-TREF"): "->" + itf}})
|
|
259
|
+
ops.append(self.access_op(m, run.name, role, f"{m.path}/{pname}", f"{itf}/{el}", pname, el))
|
|
260
|
+
notes.append(f"split '{rest}' as port '{pname}' + element '{el}'; connect the new port in the composition "
|
|
261
|
+
"and map it to a signal if it is inter-ECU")
|
|
262
|
+
return ops, "; ".join(notes)
|
|
263
|
+
|
|
264
|
+
def element_values(self, want: str, el: str, tref: str, kind: str) -> dict:
|
|
265
|
+
v = {"SHORT-NAME": el, "TYPE-TREF": "->" + tref}
|
|
266
|
+
if kind in ("Send", "Receive"):
|
|
267
|
+
v["SW-DATA-DEF-PROPS"] = {"SW-DATA-DEF-PROPS-VARIANTS": {"SW-DATA-DEF-PROPS-CONDITIONAL": {
|
|
268
|
+
"SW-IMPL-POLICY": "QUEUED"}}}
|
|
269
|
+
return v
|
|
270
|
+
|
|
271
|
+
def new_element(self, itf: str, want: str, el: str, kind: str) -> list:
|
|
272
|
+
tref, _assumed = self._type_ref(el)
|
|
273
|
+
v = self.element_values(want, el, tref, kind)
|
|
274
|
+
sn = v.pop("SHORT-NAME")
|
|
275
|
+
return [{"op": "add", "parent_path": itf, "element": "VARIABLE-DATA-PROTOTYPE" if want == "sr"
|
|
276
|
+
else "PARAMETER-DATA-PROTOTYPE", "collection": "DATA-ELEMENTS" if want == "sr" else "PARAMETERS",
|
|
277
|
+
"short_name": sn, "values": v}]
|
|
278
|
+
|
|
279
|
+
def interface_package(self, m: SwcModel) -> str:
|
|
280
|
+
for p in m.ports:
|
|
281
|
+
if p.get("interface") and self.ix.get(p["interface"]) is not None:
|
|
282
|
+
return p["interface"].rsplit("/", 1)[0]
|
|
283
|
+
return m.path.rsplit("/", 1)[0]
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def explain(m: SwcModel, name: str) -> str:
|
|
287
|
+
from .rte_api import explain_unknown
|
|
288
|
+
return explain_unknown(m, name)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
# ---------------------------------------------------------------- verify
|
|
292
|
+
def _swc_by_include(models: list, includes: list) -> Optional[SwcModel]:
|
|
293
|
+
inc = {os.path.basename(i).lower() for i in includes}
|
|
294
|
+
hits = [m for m in models if f"rte_{m.name}.h".lower() in inc]
|
|
295
|
+
return hits[0] if len(hits) == 1 else None
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def verify(root: str, files: Optional[list] = None, diff_against: Optional[str] = None, swc: Optional[str] = None,
|
|
299
|
+
runnable: Optional[str] = None, check_ops: bool = True, project=None) -> dict:
|
|
300
|
+
notes: list = []
|
|
301
|
+
ix = ArIndex(root)
|
|
302
|
+
models = build_all(root, ix)
|
|
303
|
+
if not models:
|
|
304
|
+
return {"error": "no SW-COMPONENT-TYPE found in the project ARXML; nothing to verify against"}
|
|
305
|
+
only = find_swc(models, swc) if swc else None
|
|
306
|
+
if swc and only is None:
|
|
307
|
+
return {"error": f"no SW-COMPONENT-TYPE {swc!r}; known: {', '.join(m.name for m in models)}"}
|
|
308
|
+
if only and runnable and only.runnable(runnable) is None:
|
|
309
|
+
return {"error": f"{only.name} has no runnable {runnable!r}; runnables: {', '.join(r.name for r in only.runnables)}"}
|
|
310
|
+
cx = CIndex(root)
|
|
311
|
+
lines_filter: dict = {}
|
|
312
|
+
if diff_against:
|
|
313
|
+
ch, err = changed_lines(root, diff_against)
|
|
314
|
+
if err:
|
|
315
|
+
return {"error": err}
|
|
316
|
+
scan = sorted(f for f in ch if f.lower().endswith(".c"))
|
|
317
|
+
lines_filter = ch
|
|
318
|
+
if not scan:
|
|
319
|
+
notes.append(f"no C changes since {diff_against}")
|
|
320
|
+
elif files:
|
|
321
|
+
scan = sorted(f for f in paths.expand(root, files, paths.is_c) if f.lower().endswith(".c"))
|
|
322
|
+
if not scan:
|
|
323
|
+
return {"error": f"paths {files} matched no .c file under {root}"}
|
|
324
|
+
else:
|
|
325
|
+
scan = [f for f in cx.cfiles if f not in cx.generated]
|
|
326
|
+
for f in scan: # files outside the default index (explicit generated files, new files)
|
|
327
|
+
if f not in cx.src:
|
|
328
|
+
from .csrc import functions
|
|
329
|
+
fs, raw = functions(f)
|
|
330
|
+
cx.src[f] = raw
|
|
331
|
+
cx.includes[f] = re.findall(r'^\s*#\s*include\s*[<"]([^>"]+)[>"]', raw, re.M)
|
|
332
|
+
for fn in fs:
|
|
333
|
+
cx.funcs.setdefault(fn.name, []).append(fn)
|
|
334
|
+
|
|
335
|
+
# function -> [(model, runnable)] by reachability from runnable entry points
|
|
336
|
+
reach: dict = {}
|
|
337
|
+
entry: dict = {}
|
|
338
|
+
for m in models:
|
|
339
|
+
for r in m.runnables:
|
|
340
|
+
fn = cx.get(r.symbol)
|
|
341
|
+
entry[(m.name, r.name)] = fn
|
|
342
|
+
if fn is None:
|
|
343
|
+
continue
|
|
344
|
+
for g in cx.reachable(fn):
|
|
345
|
+
reach.setdefault(id(g), []).append((m, r))
|
|
346
|
+
sugg = Suggest(ix, models)
|
|
347
|
+
findings, other = [], []
|
|
348
|
+
counts = {"OK": 0, "NOT-ALLOWED": 0, "INVENTED": 0}
|
|
349
|
+
for f in scan:
|
|
350
|
+
lf = lines_filter.get(f, None) if diff_against else None
|
|
351
|
+
for fn in sorted((g for lst in cx.funcs.values() for g in lst if g.file == f), key=lambda g: g.line):
|
|
352
|
+
owners = reach.get(id(fn), [])
|
|
353
|
+
for name, line, args in fn.rte:
|
|
354
|
+
if diff_against and lf is not None and line not in lf:
|
|
355
|
+
continue
|
|
356
|
+
cands = owners
|
|
357
|
+
if only is not None:
|
|
358
|
+
cands = [(m, r) for m, r in owners if m is only] or ([(only, None)] if not owners else [])
|
|
359
|
+
if not cands:
|
|
360
|
+
continue # reachable only from other SW-Cs
|
|
361
|
+
if runnable:
|
|
362
|
+
cands = [(m, r) for m, r in cands if r is not None and runnable in (r.name, r.symbol)]
|
|
363
|
+
if not cands:
|
|
364
|
+
continue
|
|
365
|
+
if not cands:
|
|
366
|
+
m = _swc_by_include(models, cx.includes.get(f, [])) or next(
|
|
367
|
+
(x for x in models if name in x.apis), None)
|
|
368
|
+
if m is None:
|
|
369
|
+
if LIFECYCLE.match(name):
|
|
370
|
+
other.append({"call": name, "at": f"{cx.rel(f)}:{line}", "function": fn.name,
|
|
371
|
+
"why": "RTE lifecycle/BSW API, not a SW-C port API"})
|
|
372
|
+
continue
|
|
373
|
+
if len(models) != 1:
|
|
374
|
+
findings.append(_finding(cx, f, line, fn, name, args, None, None, "INVENTED",
|
|
375
|
+
"no SW-C in the project declares this API, and the calling "
|
|
376
|
+
"function belongs to no SW-C (no runnable reaches it, no "
|
|
377
|
+
"Rte_<Swc>.h include)", None))
|
|
378
|
+
counts["INVENTED"] += 1
|
|
379
|
+
continue
|
|
380
|
+
m = models[0]
|
|
381
|
+
cands = [(m, None)]
|
|
382
|
+
for m, r in cands:
|
|
383
|
+
status, e, why = m.classify(name, r.name if r else None)
|
|
384
|
+
if r is None:
|
|
385
|
+
why += f"; {fn.name} is not reached from any runnable of {m.name}, checked at SW-C level"
|
|
386
|
+
d = _finding(cx, f, line, fn, name, args, m, r, status, why, e)
|
|
387
|
+
if status != "OK" and r is not None:
|
|
388
|
+
ops, hint = (sugg.for_entry(m, e, r.name) if e is not None else sugg.for_invented(m, name, r.name))
|
|
389
|
+
if ops:
|
|
390
|
+
d["arxml_edit"] = ops
|
|
391
|
+
if hint:
|
|
392
|
+
d["hint"] = hint
|
|
393
|
+
elif status == "INVENTED":
|
|
394
|
+
_ops, hint = sugg.for_invented(m, name, None)
|
|
395
|
+
if hint:
|
|
396
|
+
d["hint"] = hint
|
|
397
|
+
counts[status] += 1
|
|
398
|
+
findings.append(d)
|
|
399
|
+
if check_ops:
|
|
400
|
+
_check_ops(root, findings, project)
|
|
401
|
+
unmapped = [{"swc": k[0], "runnable": k[1], "symbol": next(r.symbol for m in models if m.name == k[0]
|
|
402
|
+
for r in m.runnables if r.name == k[1])}
|
|
403
|
+
for k, v in entry.items() if v is None and (only is None or k[0] == only.name)]
|
|
404
|
+
res = {"scanned": {"files": [cx.rel(f) for f in scan], "functions": sum(len(v) for v in cx.funcs.values()),
|
|
405
|
+
"calls": sum(counts.values())},
|
|
406
|
+
"counts": counts, "findings": findings,
|
|
407
|
+
"swcs": [m.name for m in ([only] if only else models)]}
|
|
408
|
+
if other:
|
|
409
|
+
res["notChecked"] = other
|
|
410
|
+
if unmapped:
|
|
411
|
+
res["runnablesWithoutCode"] = unmapped
|
|
412
|
+
if notes:
|
|
413
|
+
res["notes"] = notes
|
|
414
|
+
return res
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def _finding(cx, f, line, fn, name, args, m, r, status, why, e) -> dict:
|
|
418
|
+
d = {"status": status, "call": f"{name}({', '.join(args)})", "api": name, "at": f"{cx.rel(f)}:{line}",
|
|
419
|
+
"function": fn.name, "swc": m.name if m else None, "runnable": r.name if r else None, "why": why}
|
|
420
|
+
if e is not None:
|
|
421
|
+
d["expected"] = e.signature
|
|
422
|
+
d["cite"] = e.cite
|
|
423
|
+
return d
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def _check_ops(root: str, findings: list, project=None):
|
|
427
|
+
"""Dry-run each finding's ops with arxml_edit so only ready-to-run suggestions are labelled as such."""
|
|
428
|
+
todo = [d for d in findings if d.get("arxml_edit")]
|
|
429
|
+
if not todo:
|
|
430
|
+
return
|
|
431
|
+
try:
|
|
432
|
+
from .arxml_edit import arxml_edit
|
|
433
|
+
from .model import Project
|
|
434
|
+
p = project or Project(root, load_input=True)
|
|
435
|
+
except Exception as ex: # noqa: BLE001
|
|
436
|
+
for d in todo:
|
|
437
|
+
d["opsChecked"] = f"not checked: {ex}"
|
|
438
|
+
return
|
|
439
|
+
seen: dict = {}
|
|
440
|
+
for d in todo[:50]:
|
|
441
|
+
key = repr(d["arxml_edit"])
|
|
442
|
+
if key not in seen:
|
|
443
|
+
r = arxml_edit(p, d["arxml_edit"], dry_run=True)
|
|
444
|
+
seen[key] = True if r.get("ok") else (r.get("error") or "") + (" " + "; ".join(r.get("problems", [])) if r.get("problems") else "")
|
|
445
|
+
d["opsChecked"] = seen[key]
|
package/python/pyproject.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ecuc-engine"
|
|
3
|
-
version = "1.0
|
|
3
|
+
version = "1.4.0-pre"
|
|
4
4
|
description = "Load, read, edit in place, validate and diff AUTOSAR ECUC ARXML (Hydron AUTOSAR pack)"
|
|
5
5
|
requires-python = ">=3.10"
|
|
6
6
|
# The core is stdlib-only; autosar-data adds a strict schema check of every edited file.
|
package/skills/autosar/SKILL.md
CHANGED
|
@@ -23,18 +23,45 @@ in `Output/` is derived from it; in-house code in `Source/` calls the generated
|
|
|
23
23
|
the param defs and patches only the changed bytes. Reading raw ARXML for context is fine; prefer `ecuc_read`.
|
|
24
24
|
2. **Validate after every edit**: `ecuc_validate` (scoped with `files`/`module` if large). Fix
|
|
25
25
|
every error you introduced before moving on; report pre-existing ones separately.
|
|
26
|
-
3. **Cite** the AUTOSAR path, the DEFINITION-REF and file:line for every element you discuss or change
|
|
26
|
+
3. **Cite** the AUTOSAR path, the DEFINITION-REF and file:line for every element you discuss or change —
|
|
27
|
+
copied from tool output (see *Citations and numbers*), never estimated.
|
|
27
28
|
4. **Generated code is an output.** Never edit `Output/` (or other generated files) to fix a
|
|
28
29
|
problem — change the configuration and regenerate.
|
|
29
30
|
5. Values come from the param defs (ranges, literals, multiplicity) and the SWS; when the defs do
|
|
30
|
-
not settle a question (cross-module consistency, handle IDs, timing), say so
|
|
31
|
-
requirement
|
|
31
|
+
not settle a question (cross-module consistency, handle IDs, timing), say so plainly. Quote the SWS
|
|
32
|
+
requirement only if you have its text from a tool result this session; otherwise describe the rule
|
|
33
|
+
without an ID — never fill the gap from memory.
|
|
34
|
+
|
|
35
|
+
## Citations and numbers (always)
|
|
36
|
+
- **Requirement IDs** (SWS_/SRS_/TPS_/ECUC_/RS_…): cite one only when it appears verbatim in a tool result
|
|
37
|
+
(e.g. the `spec:` line of `autosar_audit`, an H2L rule message, `autosar_spec_id_check`) or in spec text you
|
|
38
|
+
actually read this session. Before citing any other ID, run `autosar_spec_id_check ids=[…]`; cite it only if it
|
|
39
|
+
is DEFINED, and describe it with the returned text. If the check is unavailable or says NOT-FOUND /
|
|
40
|
+
MENTIONED-ONLY, describe the rule without an ID. An invented or misremembered ID is worse than none.
|
|
41
|
+
- **No requirement IDs for plain value questions** ("what is X", "which file sets Y", "trace Z"): answer with the
|
|
42
|
+
value, its AUTOSAR path and file:line. Add an ID only when the user asks why/what the standard requires.
|
|
43
|
+
- **file:line** must come from a tool result or a file you read: for a value use `"@at:<Param>"` (the VALUE
|
|
44
|
+
element) from `ecuc_read`/`ecuc_find`, or the `@ file:line` of a `VALUES` line from `autosar_trace` /
|
|
45
|
+
`autosar_config_lookup`; `@at` / `at` is the container. A graph `file:line` of an element not in the local
|
|
46
|
+
checkout is where the element starts: read the file to find the value line. Never estimate or offset a line.
|
|
47
|
+
- **Numbers and units**: never convert bases or units in your head. Copy `"@hex:<Param>"` / `"@dec:<Param>"`
|
|
48
|
+
(and the `dec …, hex …` of VALUES lines, CAN IDs, audit `…Hex` evidence). For any other conversion or
|
|
49
|
+
arithmetic (bytes ↔ bits, ms ↔ s, address + offset), compute it with a tool or say it is not computed.
|
|
50
|
+
- **Parameter semantics**: explain what a parameter means only from its param def (`ecuc_read schema=true`,
|
|
51
|
+
the def `DESC`) or spec text you read; if neither covers it, say the meaning is not in the loaded defs/specs
|
|
52
|
+
rather than inventing one.
|
|
53
|
+
- **Simple look-ups get short, exact answers**: the value (with hex if integer), the AUTOSAR path, the
|
|
54
|
+
DEFINITION-REF and file:line — no spec background, no padding.
|
|
32
55
|
|
|
33
56
|
## Tool map
|
|
34
57
|
| Need | Tool |
|
|
35
58
|
|---|---|
|
|
36
59
|
| What modules/files/defs exist | `ecuc_read` (no path) |
|
|
37
60
|
| Values of a module/container | `ecuc_read path=… depth=…` |
|
|
61
|
+
| All containers matching a condition / referencing X / referenced by Y | `ecuc_find` |
|
|
62
|
+
| Cross-check one module against the extract, C types and memory | `ecuc_crosscheck module=…` |
|
|
63
|
+
| Edit System extract / SWC / interface XML | `arxml_edit` (DEST inferred, schema-ordered, refs checked) |
|
|
64
|
+
| Add a signal end to end (ECUC + extract + DBC) | `autosar_add_signal` |
|
|
38
65
|
| What may be set/added here (types, ranges, literals, multiplicity) | `ecuc_read path=… schema=true`, or a def path `/AUTOSAR/EcucDefs/<Mod>/…` |
|
|
39
66
|
| Change config | `ecuc_edit ops=[…]` (`dry_run` first for large edits) |
|
|
40
67
|
| Check config | `ecuc_validate` |
|
|
@@ -42,14 +69,39 @@ in `Output/` is derived from it; in-house code in `Source/` calls the generated
|
|
|
42
69
|
| Which C symbols does this config produce, who calls them / where does this symbol come from | `autosar_trace query=…` |
|
|
43
70
|
| Refs in/out, children of an element in the ingested repo | `autosar_config_lookup path=…` |
|
|
44
71
|
| Review C code that calls Com/NvM/Dem/PduR/RTE APIs | `autosar_audit` (first), then confirm with `autosar_trace` / `ecuc_read` |
|
|
45
|
-
|
|
|
72
|
+
| Context for implementing / changing a runnable (allowed RTE API, code, callees, config, similar code/changes) | `autosar_build_context swc=… runnable=… task=…` |
|
|
73
|
+
| Check that C only calls the RTE APIs its runnable may call (OK / NOT-ALLOWED / INVENTED + ARXML fix ops) | `autosar_verify_rte` (`diff_against=<ref>` for a change) |
|
|
74
|
+
| Generate BSW config code ourselves (NvM/Fee/MemIf/Dem/PduR/CanIf/Port/Dio) | `autosar_codegen`, then `autosar_codegen_diff reference_dir=Output` (see `references/codegen.md`) |
|
|
75
|
+
| Regenerate code with the vendor tool | `autosar_generate` (configured command, asks permission) |
|
|
46
76
|
| Compile/link | `autosar_build` |
|
|
77
|
+
| Is this requirement ID real, and what does it say? | `autosar_spec_id_check ids=[…]` (needs `specDocs` / `$AUTOSAR_SPEC_DOCS`) |
|
|
47
78
|
| Spec questions (SWS/TPS requirement text) | the project's ingested docs search tools |
|
|
48
79
|
|
|
80
|
+
## Workflow (validation and fix tasks)
|
|
81
|
+
1. `ecuc_validate` unfiltered (all rules). It is **necessary but not sufficient**.
|
|
82
|
+
2. Make a todo checklist with one item per module present (Com, PduR, CanIf, EcuC, NvM/Fee/Ea, Dem, Os, Rte, …)
|
|
83
|
+
and run `ecuc_crosscheck` for each; read its NOT CHECKED notes and close those gaps by hand.
|
|
84
|
+
3. `autosar_audit` for how the C code uses the configuration (handles, directions, widths, RAM blocks).
|
|
85
|
+
4. Fix only with `ecuc_edit` / `arxml_edit` / `autosar_add_signal`; then `autosar_generate`, `autosar_build`, and
|
|
86
|
+
re-run `ecuc_validate` (+ `checkGenerated: true` to prove `Output/` is not stale).
|
|
87
|
+
5. **Never conclude "no faults" from a single validate call**, and never finish with a failing validation: after
|
|
88
|
+
ANY edit run `ecuc_validate`; if it fails, fix it before answering.
|
|
89
|
+
|
|
49
90
|
## The loop
|
|
50
91
|
`ecuc_read (schema) → ecuc_edit → ecuc_validate → autosar_generate → arxml_diff / inspect Output → autosar_build → autosar_trace (impact on Source/)`.
|
|
51
92
|
Details and failure handling: `references/workflow.md`.
|
|
52
93
|
|
|
94
|
+
## Implement or change a runnable (SW-C application code)
|
|
95
|
+
1. `autosar_build_context swc=<Swc> runnable=<Runnable> task="<the task verbatim>"` FIRST. Section 3 lists the RTE APIs
|
|
96
|
+
the runnable is ALLOWED to call, with the exact declarations from `Rte_<Swc>.h` (SWS_RTE naming and signatures);
|
|
97
|
+
sections 4–6 hold its code, callees and the Com/NvM config behind its ports; 7–8 are fuzzy hints only.
|
|
98
|
+
2. Write the code using **only** the allowed APIs and their exact signatures (value vs pointer per SWS_Rte_01017/01019).
|
|
99
|
+
Never invent an `Rte_*` name; never call an API that belongs to another runnable.
|
|
100
|
+
3. `autosar_verify_rte runnable=<Runnable>` (or `diff_against=<ref>` for the whole change).
|
|
101
|
+
4. On INVENTED / NOT-ALLOWED: rewrite with an allowed API; or, only when the behaviour truly needs new data, apply the
|
|
102
|
+
returned `arxml_edit` ops (access point / port / data element), run `autosar_generate`, and verify again.
|
|
103
|
+
5. `autosar_build`. Never finish with a failing verify or build. Details: `references/runnable-code.md`.
|
|
104
|
+
|
|
53
105
|
## Integration review recipe (C code against the configuration)
|
|
54
106
|
1. `autosar_audit` (scope with `paths` if asked about specific files). It resolves every BSW call's handle
|
|
55
107
|
through the ECUC and reports raw numeric handles, unknown handles, direction mismatches, C-width vs
|
|
@@ -58,9 +110,17 @@ Details and failure handling: `references/workflow.md`.
|
|
|
58
110
|
`autosar_trace <symbol>` for the owning I-PDU, generated symbol and other callers.
|
|
59
111
|
3. Review what the audit lists as *unchecked* by hand (pointer arguments, expressions as handles), and
|
|
60
112
|
look for logic issues it cannot see (wrong signal chosen although types match, missing error handling).
|
|
61
|
-
4. Report: file:line, the call, the configured facts (AUTOSAR paths + values
|
|
113
|
+
4. Report: file:line, the call, the configured facts (AUTOSAR paths + values with their `…At` lines), the spec
|
|
114
|
+
ID exactly as the audit's `spec:` line gives it (never add others), the fix.
|
|
62
115
|
|
|
63
|
-
Facts that decide common review calls
|
|
116
|
+
Facts that decide common review calls (the IDs below are checked against R25-11; still cite one only when a
|
|
117
|
+
tool result this session shows it, per *Citations and numbers*):
|
|
118
|
+
- A Com signal's layout must match the System extract it came from (ISignalToIPduMapping startPosition /
|
|
119
|
+
packingByteOrder, ISignal length) and an EcuC Pdu's PduLength the system I-PDU length (H2L-SYS-001/002).
|
|
120
|
+
- A PduR routing path forwards one I-PDU: source and destination must stand for the same system I-PDU (or the same
|
|
121
|
+
I-SIGNAL content) — a gateway destination carrying a different I-PDU is wrong (H2L-PDUR-001).
|
|
122
|
+
- NvMNvBlockLength is the NV *data* length and must equal sizeof(the RAM block object); the Fee/Ea block must hold it
|
|
123
|
+
(H2L-NVM-001, sizeof measured by compiling a probe with the project's compiler and flags).
|
|
64
124
|
- **Direction lives on the I-PDU**, not the signal: a ComSignal is TX or RX through the `ComIPduDirection`
|
|
65
125
|
of the ComIPdu that references it (`ComIPduSignalRef`). Sending an RX signal (or receiving a TX one)
|
|
66
126
|
fails at runtime (ECUC_Com_00493, SWS_Com_00197/00198).
|
|
@@ -77,8 +137,20 @@ Facts that decide common review calls:
|
|
|
77
137
|
- `references/symbolic-names.md` — the symbolic-name rule (TPS_ECUC_02108) that links config to generated `#define`s, handle IDs.
|
|
78
138
|
- `references/config-classes.md` — pre-compile / link-time / post-build config classes, variants, post-build loadable/selectable.
|
|
79
139
|
- `references/project-layout.md` — Config / Input / Output / Source / Build / SIP and `.hydron/autosar.json`.
|
|
140
|
+
- `references/codegen.md` — deterministic code generation, handle-ID write-back, bindings, reading the semantic diff, MCAL hook.
|
|
141
|
+
- `references/runnable-code.md` — RTE API tiers (allowed / generated / derivable), naming and signature rules, verify statuses and fixes, context sections and budget.
|
|
80
142
|
- `references/workflow.md` — the edit → validate → generate → build → trace loop, diagnostics rules, and what to do when a step fails.
|
|
81
143
|
|
|
144
|
+
## Impact questions
|
|
145
|
+
Include, besides the trace to code: the **storage capacity** check (H2L-MEM-001, NvM/Fee/Ea vs MemAcc/Mem area) and
|
|
146
|
+
**NvM stored-data compatibility** — a changed RAM block size or layout makes existing NV data unreadable unless the
|
|
147
|
+
block is migrated (NvMResistantToChangedSw, NvMStaticBlockIDCheck, a new block ID or a data-version field).
|
|
148
|
+
|
|
149
|
+
## MCAL questions
|
|
150
|
+
Public AUTOSAR documents define the MCAL interfaces and parameter definitions; vendor-specific configuration details
|
|
151
|
+
(register mapping, clock trees, pin multiplexing) live in the silicon vendor's MCAL user manuals, often under NDA. When
|
|
152
|
+
the ingested docs do not cover a detail, say so and point to the vendor MCAL manual instead of guessing.
|
|
153
|
+
|
|
82
154
|
## Scoping and empty results
|
|
83
155
|
- Path arguments (`ecuc_validate files`, `autosar_audit paths`, `arxml_diff a/b`, `.hydron/autosar.json`
|
|
84
156
|
globs) take files, directories (recursive) or globs (`**/Com*.arxml`), relative to the project root or
|