@h2loop/autosar 1.0.1 → 1.1.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 +4 -0
- package/agents/autosar-integrator.md +34 -2
- package/package.json +4 -1
- package/plugins/index.js +53 -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 +328 -48
- package/python/ecuc_engine/cli.py +114 -5
- package/python/ecuc_engine/codegen/__init__.py +185 -0
- package/python/ecuc_engine/codegen/binding.py +210 -0
- package/python/ecuc_engine/codegen/bindings/sip_standin.json +111 -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 +395 -0
- package/python/ecuc_engine/codegen/handles.py +89 -0
- package/python/ecuc_engine/codegen/mcal/tc4d.json +60 -0
- package/python/ecuc_engine/codegen/model.py +259 -0
- package/python/ecuc_engine/codegen/modules/__init__.py +7 -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 +133 -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/find.py +162 -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/sysmodel.py +152 -0
- package/python/ecuc_engine/validate.py +15 -2
- package/python/ecuc_engine/verify_rte.py +445 -0
- package/python/pyproject.toml +1 -1
- package/skills/autosar/SKILL.md +47 -1
- package/skills/autosar/references/codegen.md +30 -0
- package/skills/autosar/references/project-layout.md +7 -1
- package/skills/autosar/references/runnable-code.md +50 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
"""The shared BSW emitter: one ModuleOutput per module, rendered into the standard file set.
|
|
2
|
+
|
|
3
|
+
File placement follows each parameter's effective config class for the module's
|
|
4
|
+
IMPLEMENTATION-CONFIG-VARIANT (from the param defs' VALUE-CONFIG-CLASSES):
|
|
5
|
+
PRE-COMPILE / PUBLISHED-INFORMATION scalars -> #defines in <Mip>_Cfg.h
|
|
6
|
+
tables whose fields are all pre-compile -> <Mip>_Cfg.c (SWS_BSW_00103 Table 5.1)
|
|
7
|
+
tables with a link-time field -> <Mip>_Lcfg.c (SWS_BSW_00013)
|
|
8
|
+
tables with a post-build field -> <Mip>_PBcfg.c (SWS_BSW_00015)
|
|
9
|
+
Everything generic is driven by the defs: scalars of singleton containers become macros, every
|
|
10
|
+
symbolicNameValue parameter a TPS_ECUC_02108 symbol, every multiplicity-n container a const table.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import hashlib
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
from .. import ENGINE_VERSION
|
|
19
|
+
from .. import defs as D
|
|
20
|
+
from . import cwriter as W
|
|
21
|
+
from .model import CLASS_FILE, CLASS_RANK, CgModel, Container, ModuleCfg, snake
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class Ctx:
|
|
26
|
+
cg: CgModel
|
|
27
|
+
headers: dict # project header basename -> path (static code, excluding the output dir)
|
|
28
|
+
bindings: dict
|
|
29
|
+
binding_name: Optional[str]
|
|
30
|
+
release: tuple
|
|
31
|
+
sources: list = field(default_factory=list) # project .c/.h files (static + in-house), for SchM scans
|
|
32
|
+
generating: set = field(default_factory=set) # module names generated in this run
|
|
33
|
+
available: set = field(default_factory=set) # header basenames reachable by the build (incl. the output dir)
|
|
34
|
+
|
|
35
|
+
def xsym(self, c: Container, here: ModuleCfg, used: set) -> str:
|
|
36
|
+
"""Symbol of a container for use in module `here`: the TPS_ECUC_02108 name when its provider header
|
|
37
|
+
(<Mip>_Cfg.h) is generated now or already exists, else the configured value (named in a comment)."""
|
|
38
|
+
sym = self.cg.symbol(c)
|
|
39
|
+
if c.module is here:
|
|
40
|
+
return sym
|
|
41
|
+
if c.module.name in self.generating or f"{c.module.mip}_Cfg.h" in self.available:
|
|
42
|
+
used.add(c.module.mip)
|
|
43
|
+
return sym
|
|
44
|
+
from ..values import parse_int
|
|
45
|
+
v = parse_int(self.cg.symbol_value(c) or "")
|
|
46
|
+
return f"{W.c_uint(v)} /* {sym} */" if v is not None else sym
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ModuleOutput:
|
|
50
|
+
def __init__(self, ctx: Ctx, mc: ModuleCfg):
|
|
51
|
+
self.ctx = ctx
|
|
52
|
+
self.mc = mc
|
|
53
|
+
self.mip = mc.mip
|
|
54
|
+
self.MIP = mc.MIP
|
|
55
|
+
self.groups: list = [] # (title, [define lines])
|
|
56
|
+
self.types: list = []
|
|
57
|
+
self.hdecls: list = []
|
|
58
|
+
self.h_includes: list = []
|
|
59
|
+
self.tables: dict = {} # rank -> [text]
|
|
60
|
+
self.c_externs: dict = {} # rank -> [text]
|
|
61
|
+
self.c_includes: list = []
|
|
62
|
+
self.used_mips: set = set()
|
|
63
|
+
self.defined: set = set()
|
|
64
|
+
self.notes: list = []
|
|
65
|
+
self.extra_files: dict = {} # name -> text (module-specific additional files)
|
|
66
|
+
self.symbols: list = [] # (name, value, container path)
|
|
67
|
+
|
|
68
|
+
# ---------- collection ----------
|
|
69
|
+
def group(self, title: str, defs: list):
|
|
70
|
+
lines = []
|
|
71
|
+
for d in defs:
|
|
72
|
+
name = d[0]
|
|
73
|
+
if name in self.defined:
|
|
74
|
+
self.notes.append(f"duplicate #define {name} suppressed ({title})")
|
|
75
|
+
continue
|
|
76
|
+
self.defined.add(name)
|
|
77
|
+
lines.append(W.define(*d))
|
|
78
|
+
if lines:
|
|
79
|
+
self.groups.append((title, lines))
|
|
80
|
+
|
|
81
|
+
def table(self, rank: int, text: str, externs: Optional[list] = None):
|
|
82
|
+
rank = max(rank, 1)
|
|
83
|
+
self.tables.setdefault(rank, []).append(text)
|
|
84
|
+
for e in externs or []:
|
|
85
|
+
lst = self.c_externs.setdefault(rank, [])
|
|
86
|
+
if e not in lst:
|
|
87
|
+
lst.append(e)
|
|
88
|
+
|
|
89
|
+
def include(self, h: str):
|
|
90
|
+
if h not in self.c_includes:
|
|
91
|
+
self.c_includes.append(h)
|
|
92
|
+
|
|
93
|
+
# ---------- rendering ----------
|
|
94
|
+
def digest(self) -> str:
|
|
95
|
+
h = hashlib.sha256(ENGINE_VERSION.encode())
|
|
96
|
+
for f in sorted({f.file for c in self.mc.containers for f in c.frags} | {self.mc.elem.file}):
|
|
97
|
+
h.update(self.ctx.cg.p.files[f].data)
|
|
98
|
+
for f in sorted(self.ctx.cg.defs.files):
|
|
99
|
+
h.update(f.rsplit("/", 1)[-1].encode())
|
|
100
|
+
return "sha256:" + h.hexdigest()[:16]
|
|
101
|
+
|
|
102
|
+
def _banner(self, name: str) -> str:
|
|
103
|
+
return W.banner(name, self.mc.name, self.mc.variant, self.ctx.release, self.ctx.cg.rel_files(self.mc),
|
|
104
|
+
self._digest)
|
|
105
|
+
|
|
106
|
+
def render(self) -> dict:
|
|
107
|
+
self._digest = self.digest()
|
|
108
|
+
out = {}
|
|
109
|
+
hname = f"{self.mip}_Cfg.h"
|
|
110
|
+
g = W.guard(hname)
|
|
111
|
+
parts = [self._banner(hname), "", f"#ifndef {g}", f"#define {g}", ""]
|
|
112
|
+
for inc in self.h_includes:
|
|
113
|
+
parts.append(f'#include "{inc}"')
|
|
114
|
+
maj, mnr, rev = self.ctx.release
|
|
115
|
+
sw = [int(x) if x.isdigit() else 0 for x in ENGINE_VERSION.split("-")[0].split(".")][:3]
|
|
116
|
+
ver = [(f"{self.MIP}_CFG_AR_RELEASE_MAJOR_VERSION", f"{maj}u"),
|
|
117
|
+
(f"{self.MIP}_CFG_AR_RELEASE_MINOR_VERSION", f"{mnr}u"),
|
|
118
|
+
(f"{self.MIP}_CFG_AR_RELEASE_REVISION_VERSION", f"{rev}u"),
|
|
119
|
+
(f"{self.MIP}_CFG_SW_MAJOR_VERSION", f"{sw[0]}u"),
|
|
120
|
+
(f"{self.MIP}_CFG_SW_MINOR_VERSION", f"{sw[1]}u"),
|
|
121
|
+
(f"{self.MIP}_CFG_SW_PATCH_VERSION", f"{sw[2]}u")]
|
|
122
|
+
parts += ["", "/* Published information of this generated configuration (SWS_BSW_00059) */"]
|
|
123
|
+
parts += [W.define(n, v) for n, v in ver]
|
|
124
|
+
for title, lines in self.groups:
|
|
125
|
+
parts += ["", f"/* {title} */", *lines]
|
|
126
|
+
if self.types:
|
|
127
|
+
parts += ["", "/* Generated configuration types (one struct per multiplicity-n container def) */"]
|
|
128
|
+
parts += self.types
|
|
129
|
+
if self.hdecls:
|
|
130
|
+
parts += ["", *self.hdecls]
|
|
131
|
+
parts += ["", f"#endif /* {g} */", ""]
|
|
132
|
+
out[hname] = "\n".join(parts)
|
|
133
|
+
for rank in sorted(self.tables):
|
|
134
|
+
fname = f"{self.mip}_{CLASS_FILE[rank]}"
|
|
135
|
+
if fname in out: # PUBLISHED-INFORMATION and PRE-COMPILE share <Mip>_Cfg.c
|
|
136
|
+
continue
|
|
137
|
+
ranks = [r for r in self.tables if CLASS_FILE[r] == CLASS_FILE[rank]]
|
|
138
|
+
body = []
|
|
139
|
+
externs = []
|
|
140
|
+
for r in sorted(ranks):
|
|
141
|
+
body += self.tables[r]
|
|
142
|
+
externs += [e for e in self.c_externs.get(r, []) if e not in externs]
|
|
143
|
+
incs = list(self.c_includes) or [f"{self.mip}_Cfg.h"]
|
|
144
|
+
for m in sorted(self.used_mips - {self.mip}):
|
|
145
|
+
h = f"{m}_Cfg.h"
|
|
146
|
+
if h not in incs:
|
|
147
|
+
incs.append(h)
|
|
148
|
+
text = [self._banner(fname), ""]
|
|
149
|
+
text += [f'#include "{i}"' for i in incs]
|
|
150
|
+
text += ["", self._version_check(), ""]
|
|
151
|
+
if externs:
|
|
152
|
+
text += ["/* Objects owned by other modules / the application, referenced by this configuration */",
|
|
153
|
+
*externs, ""]
|
|
154
|
+
text += [W.memmap(self.mip, "\n\n".join(body)), ""]
|
|
155
|
+
out[fname] = "\n".join(text)
|
|
156
|
+
out.update(self.extra_files)
|
|
157
|
+
return out
|
|
158
|
+
|
|
159
|
+
def _version_check(self) -> str:
|
|
160
|
+
M = self.MIP
|
|
161
|
+
return "\n".join([
|
|
162
|
+
f"/* SWS_BSW_00036: the static code ({self.mip}.h) and this configuration must use the same AUTOSAR release */",
|
|
163
|
+
f"#if defined({M}_AR_RELEASE_MAJOR_VERSION) && defined({M}_AR_RELEASE_MINOR_VERSION)",
|
|
164
|
+
f"#if ({M}_AR_RELEASE_MAJOR_VERSION != {M}_CFG_AR_RELEASE_MAJOR_VERSION) || "
|
|
165
|
+
f"({M}_AR_RELEASE_MINOR_VERSION != {M}_CFG_AR_RELEASE_MINOR_VERSION)",
|
|
166
|
+
f'#error "{self.mip}: AUTOSAR release of the generated configuration does not match {self.mip}.h"',
|
|
167
|
+
"#endif",
|
|
168
|
+
"#endif",
|
|
169
|
+
])
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# ======================================================================================
|
|
173
|
+
# Generic emission, driven only by the param defs
|
|
174
|
+
# ======================================================================================
|
|
175
|
+
|
|
176
|
+
def strip_prefix(mip: str, name: str) -> str:
|
|
177
|
+
return name[len(mip):] if name.lower().startswith(mip.lower()) and len(name) > len(mip) else name
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def scalar_macros(cg: CgModel, mo: ModuleOutput):
|
|
181
|
+
"""PRE-COMPILE / PUBLISHED-INFORMATION params of singleton containers -> #define <MIP>_<PARAM>."""
|
|
182
|
+
mc = mo.mc
|
|
183
|
+
names: dict = {}
|
|
184
|
+
for c in mc.containers:
|
|
185
|
+
if cg.is_multi(c) or c.defn is None:
|
|
186
|
+
continue
|
|
187
|
+
lines = []
|
|
188
|
+
for pd_path in c.defn.params:
|
|
189
|
+
d = cg.defs.get(pd_path)
|
|
190
|
+
if d is None or d.upper != 1 or cg.eff_class(mc, d) > CLASS_RANK["PRE-COMPILE"]:
|
|
191
|
+
continue
|
|
192
|
+
raw = c.text(d.name)
|
|
193
|
+
if raw is None:
|
|
194
|
+
raw = d.default
|
|
195
|
+
if raw is None:
|
|
196
|
+
continue
|
|
197
|
+
base = f"{mo.MIP}_{snake(strip_prefix(mo.mip, d.name))}"
|
|
198
|
+
if base in names and names[base] != c.path:
|
|
199
|
+
base = f"{mo.MIP}_{snake(strip_prefix(mo.mip, c.def_name))}_{snake(strip_prefix(mo.mip, d.name))}"
|
|
200
|
+
names[base] = c.path
|
|
201
|
+
lines.append((base, W.macro_value(d, raw)))
|
|
202
|
+
if d.kind == "ENUMERATION":
|
|
203
|
+
enum_literals(mo, d)
|
|
204
|
+
mo.group(f"Pre-compile parameters of {c.def_name} ({c.path})", lines)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def enum_literals(mo: ModuleOutput, d: D.DefNode):
|
|
208
|
+
"""Guarded values for module-prefixed enum literals used in #if comparisons (def literal order)."""
|
|
209
|
+
lits = [x for x in d.literals if x.upper().startswith(mo.MIP + "_")]
|
|
210
|
+
if not lits or len(lits) != len(d.literals):
|
|
211
|
+
return
|
|
212
|
+
key = f"__enum__{d.path}"
|
|
213
|
+
if key in mo.defined:
|
|
214
|
+
return
|
|
215
|
+
mo.defined.add(key)
|
|
216
|
+
lines = [f"#ifndef {lit}\n" + W.define(lit, W.c_uint(i)) + "\n#endif" for i, lit in enumerate(d.literals)]
|
|
217
|
+
mo.groups.append((f"Literals of {d.name} (def literal order; the static code may predefine them)", lines))
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def symbolic_names(cg: CgModel, mo: ModuleOutput):
|
|
221
|
+
"""TPS_ECUC_02108 / SWS_BSW_00200 symbols for every symbolicNameValue parameter of the module."""
|
|
222
|
+
by_def: dict = {}
|
|
223
|
+
for c in mo.mc.containers:
|
|
224
|
+
d = cg.symbolic_param(c)
|
|
225
|
+
if d is None:
|
|
226
|
+
continue
|
|
227
|
+
v = c.text(d.name)
|
|
228
|
+
if v is None:
|
|
229
|
+
continue
|
|
230
|
+
by_def.setdefault(c.def_name, []).append((c, d, v))
|
|
231
|
+
for dname, items in by_def.items():
|
|
232
|
+
lines = []
|
|
233
|
+
items = sorted(items, key=lambda t: (cg.sorted_instances(mo.mc, dname).index(t[0])))
|
|
234
|
+
for c, d, v in items:
|
|
235
|
+
sym = cg.symbol(c)
|
|
236
|
+
val = W.macro_value(d, v)
|
|
237
|
+
lines.append((sym, val))
|
|
238
|
+
mo.symbols.append((sym, val, c.path))
|
|
239
|
+
mo.group(f"Symbolic names of {dname} (TPS_ECUC_02108, {items[0][1].name})", lines)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# ---------- generic tables ----------
|
|
243
|
+
|
|
244
|
+
@dataclass
|
|
245
|
+
class Field:
|
|
246
|
+
name: str
|
|
247
|
+
ctype: str
|
|
248
|
+
steps: list # sub-container def names to the owner of the param
|
|
249
|
+
d: Optional[D.DefNode] = None # param/ref def
|
|
250
|
+
choice: Optional[D.DefNode] = None # for the choice selector field
|
|
251
|
+
parent: bool = False
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _fields(cg: CgModel, cdef: D.DefNode, prefix: str = "", steps: Optional[list] = None, depth: int = 0) -> list:
|
|
255
|
+
steps = steps or []
|
|
256
|
+
out: list = []
|
|
257
|
+
skipped: list = []
|
|
258
|
+
for p in cdef.params + cdef.refs:
|
|
259
|
+
d = cg.defs.get(p)
|
|
260
|
+
if d is None:
|
|
261
|
+
continue
|
|
262
|
+
if d.upper != 1:
|
|
263
|
+
if d.kind == "ENUMERATION" and 0 < len(d.literals) <= 32:
|
|
264
|
+
out.append(Field(prefix + d.name + "Mask", "uint32", steps, d)) # set of literals as a bitmask
|
|
265
|
+
else:
|
|
266
|
+
skipped.append(f"{d.name} (multi-valued)")
|
|
267
|
+
continue
|
|
268
|
+
if D.is_ref_kind(d.kind or ""):
|
|
269
|
+
if d.kind not in ("REFERENCE", "SYMBOLIC-NAME-REFERENCE", "CHOICE-REFERENCE"):
|
|
270
|
+
skipped.append(f"{d.name} ({d.kind.lower()})")
|
|
271
|
+
continue
|
|
272
|
+
out.append(Field(prefix + d.name, "uint16", steps, d))
|
|
273
|
+
continue
|
|
274
|
+
ct = W.param_ctype(d)
|
|
275
|
+
if ct is None:
|
|
276
|
+
skipped.append(f"{d.name} ({d.kind.lower()})")
|
|
277
|
+
continue
|
|
278
|
+
out.append(Field(prefix + d.name, ct, steps, d))
|
|
279
|
+
if depth < 4:
|
|
280
|
+
for sp in cdef.containers:
|
|
281
|
+
sd = cg.defs.get(sp)
|
|
282
|
+
if sd is None or sd.upper != 1:
|
|
283
|
+
continue # multiplicity-n sub-containers get their own table
|
|
284
|
+
if sd.tag == D.CHOICE_TAG:
|
|
285
|
+
out.append(Field(prefix + sd.name, "uint8", steps + [sd.name], choice=sd))
|
|
286
|
+
for bp in sd.containers:
|
|
287
|
+
bd = cg.defs.get(bp)
|
|
288
|
+
if bd is not None:
|
|
289
|
+
f2 = _fields(cg, bd, f"{prefix}{sd.name}_{bd.name}_", steps + [sd.name, bd.name], depth + 1)
|
|
290
|
+
out += f2
|
|
291
|
+
continue
|
|
292
|
+
out += _fields(cg, sd, f"{prefix}{sd.name}_", steps + [sd.name], depth + 1)
|
|
293
|
+
return out
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _owner(c: Optional[Container], steps: list) -> Optional[Container]:
|
|
297
|
+
for s in steps:
|
|
298
|
+
if c is None:
|
|
299
|
+
return None
|
|
300
|
+
subs = c.sub(s)
|
|
301
|
+
c = subs[0] if subs else None
|
|
302
|
+
return c
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _value(cg: CgModel, mo: ModuleOutput, row: Container, f: Field) -> tuple:
|
|
306
|
+
if f.parent:
|
|
307
|
+
p = row.parent
|
|
308
|
+
while p is not None and not cg.is_multi(p):
|
|
309
|
+
p = p.parent
|
|
310
|
+
return (W.c_uint(cg.index_of(p)) if p is not None else "0u", p.name if p is not None else "")
|
|
311
|
+
owner = _owner(row, f.steps)
|
|
312
|
+
if f.choice is not None:
|
|
313
|
+
if owner is None or not owner.children:
|
|
314
|
+
return ("0xFFu", "no choice")
|
|
315
|
+
br = owner.children[0]
|
|
316
|
+
names = [x.rsplit("/", 1)[-1] for x in f.choice.containers]
|
|
317
|
+
return (W.c_uint(names.index(br.def_name)) if br.def_name in names else "0xFFu", br.def_name)
|
|
318
|
+
d = f.d
|
|
319
|
+
if D.is_ref_kind(d.kind or ""):
|
|
320
|
+
t = cg.target(owner.value(d.name)) if owner is not None else None
|
|
321
|
+
if t is None:
|
|
322
|
+
return ("0xFFFFu", "no reference")
|
|
323
|
+
sym = cg.symbol(t)
|
|
324
|
+
if sym is not None:
|
|
325
|
+
return (mo.ctx.xsym(t, mo.mc, mo.used_mips), "")
|
|
326
|
+
if cg.is_multi(t):
|
|
327
|
+
return (W.c_uint(cg.index_of(t)), t.name)
|
|
328
|
+
return ("0u", t.name)
|
|
329
|
+
if d.upper != 1: # multi-valued enumeration -> bitmask of def literal indices
|
|
330
|
+
lits = [pv.value for pv in owner.values(d.name)] if owner is not None else []
|
|
331
|
+
mask = 0
|
|
332
|
+
for x in lits:
|
|
333
|
+
if x in d.literals:
|
|
334
|
+
mask |= 1 << d.literals.index(x)
|
|
335
|
+
return (W.c_uint(mask, hexa=True), "|".join(lits))
|
|
336
|
+
raw = owner.text(d.name) if owner is not None else None
|
|
337
|
+
return W.table_value(d, raw)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def generic_tables(cg: CgModel, mo: ModuleOutput, skip: set):
|
|
341
|
+
"""One const struct array per multiplicity-n container def (plus singleton structs for non-PC scalars)."""
|
|
342
|
+
mc = mo.mc
|
|
343
|
+
seen: list = []
|
|
344
|
+
for c in mc.containers:
|
|
345
|
+
if c.def_name in skip or c.defn is None or c.def_name in seen:
|
|
346
|
+
continue
|
|
347
|
+
if c.defn.upper == 1 and not _has_dynamic(cg, mc, c):
|
|
348
|
+
continue
|
|
349
|
+
if c.defn.upper == 1 and cg.is_multi(c):
|
|
350
|
+
continue # flattened into its multi ancestor's row
|
|
351
|
+
seen.append(c.def_name)
|
|
352
|
+
for dname in seen:
|
|
353
|
+
rows = cg.sorted_instances(mc, dname)
|
|
354
|
+
cdef = rows[0].defn
|
|
355
|
+
fields = _fields(cg, cdef)
|
|
356
|
+
if rows[0].parent is not None and cg.is_multi(rows[0].parent):
|
|
357
|
+
fields = [Field("Parent", "uint16", [], parent=True)] + fields
|
|
358
|
+
if not fields:
|
|
359
|
+
continue
|
|
360
|
+
tname = f"{mo.mip}_Cfg{dname}Type"
|
|
361
|
+
oname = f"{mo.mip}_Cfg{dname}"
|
|
362
|
+
rank = max([cg.eff_class(mc, f.d) for f in fields if f.d is not None] + [cg.eff_class(mc, cdef), 1])
|
|
363
|
+
mo.types.append("typedef struct {\n" + "\n".join(f" {f.ctype} {f.name};" for f in fields) +
|
|
364
|
+
f"\n}} {tname};")
|
|
365
|
+
lines = []
|
|
366
|
+
for r in rows:
|
|
367
|
+
vals = []
|
|
368
|
+
notes = []
|
|
369
|
+
for f in fields:
|
|
370
|
+
v, n = _value(cg, mo, r, f)
|
|
371
|
+
vals.append(v)
|
|
372
|
+
sym = cg.symbol(r)
|
|
373
|
+
lines.append(W.initializer(vals, sym or r.name))
|
|
374
|
+
multi = cdef.upper != 1 or len(rows) > 1
|
|
375
|
+
cnt = f"{mo.MIP}_CFG_NUM_{snake(strip_prefix(mo.mip, dname))}"
|
|
376
|
+
if multi:
|
|
377
|
+
mo.group(f"Size of {oname}[]", [(cnt, W.c_uint(len(rows)))])
|
|
378
|
+
decl = f"CONST({tname}, {mo.MIP}_CONST) {oname}[{cnt}]"
|
|
379
|
+
body = f"{decl} = {{\n" + ",\n".join(lines) + "\n};"
|
|
380
|
+
else:
|
|
381
|
+
decl = f"CONST({tname}, {mo.MIP}_CONST) {oname}"
|
|
382
|
+
body = f"{decl} =\n" + lines[0].strip() + ";"
|
|
383
|
+
mo.hdecls.append(f"extern {decl};")
|
|
384
|
+
names = ", ".join(f.name for f in fields)
|
|
385
|
+
mo.table(rank, f"/* {dname}: {len(rows)} row(s), fields {names} */\n{body}")
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def _has_dynamic(cg: CgModel, mc: ModuleCfg, c: Container) -> bool:
|
|
389
|
+
"""A singleton container holding link-time/post-build scalars needs a const object, not macros."""
|
|
390
|
+
for p in c.defn.params:
|
|
391
|
+
d = cg.defs.get(p)
|
|
392
|
+
if d is not None and d.upper == 1 and W.param_ctype(d) and cg.eff_class(mc, d) > CLASS_RANK["PRE-COMPILE"]:
|
|
393
|
+
if c.text(d.name) is not None:
|
|
394
|
+
return True
|
|
395
|
+
return False
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Deterministic handle-ID assignment for symbolicNameValue parameters, written back in place.
|
|
2
|
+
|
|
3
|
+
TPS_ECUC_02107: values of symbolicNameValue parameters that the configuration editor or module
|
|
4
|
+
generator assigns are stored in the ECUC XML. TPS_ECUC_02108 turns them into the
|
|
5
|
+
`<Mip>Conf_<ContainerDef>_<ContainerValue>` #defines.
|
|
6
|
+
|
|
7
|
+
Algorithm (stable by construction):
|
|
8
|
+
* values already in the ECUC are kept as they are (so regeneration never renumbers a handle that
|
|
9
|
+
code or another ECU already uses), duplicates among them are reported, not "fixed";
|
|
10
|
+
* containers without a value get the lowest free integers >= max(def MIN, 0), in document order;
|
|
11
|
+
* the scope is one container def within one module (the symbol namespace of TPS_ECUC_02108).
|
|
12
|
+
Only INTEGER parameters are assigned. A missing STRING symbolic value is reported.
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
from ..values import parse_int
|
|
19
|
+
from .model import CgModel, ModuleCfg
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def plan(cg: CgModel, modules: list) -> dict:
|
|
23
|
+
"""-> {"assign": [{path, param, value, def}], "errors": [...], "notes": [...]}"""
|
|
24
|
+
out: dict = {"assign": [], "errors": [], "notes": []}
|
|
25
|
+
for name in modules:
|
|
26
|
+
mc = cg.module(name)
|
|
27
|
+
if mc is None:
|
|
28
|
+
continue
|
|
29
|
+
_plan_module(cg, mc, out)
|
|
30
|
+
return out
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _plan_module(cg: CgModel, mc: ModuleCfg, out: dict):
|
|
34
|
+
seen_defs: list = []
|
|
35
|
+
for c in mc.containers:
|
|
36
|
+
if c.def_name not in seen_defs:
|
|
37
|
+
seen_defs.append(c.def_name)
|
|
38
|
+
for dname in seen_defs:
|
|
39
|
+
insts = cg.instances(mc, dname)
|
|
40
|
+
d = cg.symbolic_param(insts[0])
|
|
41
|
+
if d is None:
|
|
42
|
+
continue
|
|
43
|
+
if d.kind != "INTEGER":
|
|
44
|
+
for c in insts:
|
|
45
|
+
if c.text(d.name) in (None, ""):
|
|
46
|
+
out["notes"].append(f"{c.path}: {d.name} (symbolic {d.kind}) has no value and cannot be "
|
|
47
|
+
"auto-assigned; its symbol is not generated")
|
|
48
|
+
continue
|
|
49
|
+
lo = max(parse_int(d.min) if d.min else 0, 0)
|
|
50
|
+
hi: Optional[int] = parse_int(d.max) if d.max else None
|
|
51
|
+
used: dict = {}
|
|
52
|
+
missing = []
|
|
53
|
+
for c in insts:
|
|
54
|
+
v = parse_int(c.text(d.name) or "")
|
|
55
|
+
if v is None:
|
|
56
|
+
missing.append(c)
|
|
57
|
+
continue
|
|
58
|
+
if v in used:
|
|
59
|
+
out["errors"].append(f"{d.name}={v} is used by both {used[v]} and {c.path} "
|
|
60
|
+
f"(handle IDs must be unique per module, TPS_ECUC_02108)")
|
|
61
|
+
used.setdefault(v, c.path)
|
|
62
|
+
nxt = lo
|
|
63
|
+
for c in missing:
|
|
64
|
+
while nxt in used:
|
|
65
|
+
nxt += 1
|
|
66
|
+
if hi is not None and nxt > hi:
|
|
67
|
+
out["errors"].append(f"{c.path}: no free {d.name} left in [{lo}..{hi}]")
|
|
68
|
+
break
|
|
69
|
+
used[nxt] = c.path
|
|
70
|
+
out["assign"].append({"path": c.path, "param": d.name, "value": nxt, "def": d.path})
|
|
71
|
+
if not d.with_auto:
|
|
72
|
+
out["notes"].append(f"{c.path}: assigned {d.name}={nxt}; the def has no withAuto, so check that "
|
|
73
|
+
"the number carries no hardware meaning")
|
|
74
|
+
nxt += 1
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def apply(project, assignments: list, write: bool) -> dict:
|
|
78
|
+
"""Write the assigned values into the ECUC ARXML in place (TPS_ECUC_02107) via the engine's editor.
|
|
79
|
+
|
|
80
|
+
write=False patches only the in-memory model (dry run), so the generated code still uses them.
|
|
81
|
+
"""
|
|
82
|
+
if not assignments:
|
|
83
|
+
return {"written": False, "changes": []}
|
|
84
|
+
from ..edit import edit
|
|
85
|
+
ops = [{"op": "set", "path": a["path"], "param": a["param"], "value": a["value"]} for a in assignments]
|
|
86
|
+
res = edit(project, ops, dry_run=not write, run_validate=False)
|
|
87
|
+
if not res.get("ok"):
|
|
88
|
+
raise RuntimeError(f"handle-ID write-back failed: {res.get('error')}")
|
|
89
|
+
return {"written": bool(res.get("written")), "changes": res.get("changes", [])}
|
|
@@ -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
|
+
}
|