@h2loop/autosar 1.2.1 → 1.3.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/package.json +1 -1
- package/python/ecuc_engine/codegen/binding.py +41 -1
- package/python/ecuc_engine/codegen/bindings/sip_standin.json +118 -0
- package/python/ecuc_engine/codegen/modules/linif.py +53 -0
- package/python/ecuc_engine/codegen/registry.py +67 -37
- package/skills/autosar/references/codegen.md +18 -0
package/package.json
CHANGED
|
@@ -19,6 +19,9 @@ Path language (a list of steps, evaluated left to right from the row container):
|
|
|
19
19
|
finds its neighbour's PDU handle (TPS_ECUC 3.4.1.3, via EcucPduCollection/Pdu)
|
|
20
20
|
call:<name> a module generator's resolver (Generator.path_steps()) for what the defs cannot
|
|
21
21
|
express as a path, e.g. Com's notification callouts (terminal, token)
|
|
22
|
+
child the one chosen sub-container of a choice container (any def)
|
|
23
|
+
def def short name of the current container (terminal), e.g. which choice is configured
|
|
24
|
+
count:<Def> number of direct sub-containers of def <Def> (terminal)
|
|
22
25
|
sym TPS_ECUC_02108 symbol of the current container (terminal)
|
|
23
26
|
module | name | index the current container's module name / SHORT-NAME / table index (terminal)
|
|
24
27
|
Macro values: count:<Def> | count:@<Set> | max:<Def>/<Param>, optionally followed by +N or -N.
|
|
@@ -29,7 +32,12 @@ Module binding keys besides tables/objects/macros:
|
|
|
29
32
|
vars RAM objects per row that tables point to (e.g. I-PDU buffers), sized by a path
|
|
30
33
|
require [{"rows": "<Def>", "path": <path>, "in": [...], "why": "..."}]: what the static code supports;
|
|
31
34
|
a configuration outside it is an error, never silently mis-generated
|
|
32
|
-
|
|
35
|
+
Table keys besides name/type/rows/fields: order (sort param), contiguous (that param must be exactly
|
|
36
|
+
0..n-1 after sorting, e.g. a position index), designated, per (<ParentDef>: one table per parent instance,
|
|
37
|
+
rows = its direct <Def> children, "{}" in name = parent SHORT-NAME; a parent with no rows is an error,
|
|
38
|
+
C has no empty arrays). unsupported entries may set "max": N (more than N containers is the error).
|
|
39
|
+
Field keys: path, default, enum (literal or token -> C token), scale (multiply, must give an integer:
|
|
40
|
+
seconds -> ms), fmt ("hex" | "int" | template with {} / {MIP} / {Mip}),
|
|
33
41
|
divide (a path from the row whose value divides this one, rounded: seconds -> main-function ticks),
|
|
34
42
|
agg ({"over": <multi-ref>, "params": [...], "op": "min_nonzero" | "max", "skipIf": <param>}).
|
|
35
43
|
"""
|
|
@@ -147,6 +155,16 @@ class Eval:
|
|
|
147
155
|
(not wdef or c.def_name == wdef)]
|
|
148
156
|
hits.sort(key=lambda c: (c.module.name, self.cg.by_path_index(c)))
|
|
149
157
|
cur = hits[0] if hits else None
|
|
158
|
+
elif op == "child":
|
|
159
|
+
kids = list(cur.children)
|
|
160
|
+
if len(kids) > 1:
|
|
161
|
+
raise BindingError(f"{cur.path}: path step child: {len(kids)} sub-containers, expected the one "
|
|
162
|
+
"chosen sub-container of a choice container")
|
|
163
|
+
cur = kids[0] if kids else None
|
|
164
|
+
elif op == "def":
|
|
165
|
+
return ("token", cur.def_name)
|
|
166
|
+
elif op == "count":
|
|
167
|
+
return ("token", W.c_uint(len(cur.sub(arg))))
|
|
150
168
|
elif op == "sym":
|
|
151
169
|
s = self.cg.symbol(cur)
|
|
152
170
|
if s and self.ctx is not None:
|
|
@@ -231,6 +249,12 @@ class Eval:
|
|
|
231
249
|
s = self.ctx.xsym(v, self.mc, self.used_mips)
|
|
232
250
|
return (s, "")
|
|
233
251
|
if kind == "token":
|
|
252
|
+
if "enum" in spec:
|
|
253
|
+
if v not in spec["enum"]:
|
|
254
|
+
raise BindingError(f"{row.path}: {spec['path']} = {v} is not supported by the static code "
|
|
255
|
+
f"(the {self.mc.name} binding maps only {', '.join(spec['enum'])})")
|
|
256
|
+
e = spec["enum"][v]
|
|
257
|
+
return (W.c_uint(e) if isinstance(e, int) else str(e), v)
|
|
234
258
|
if fmt and fmt not in ("hex", "int"):
|
|
235
259
|
return (fmt.replace("{MIP}", v.upper()).replace("{Mip}", v).replace("{}", v), "")
|
|
236
260
|
return (v, "")
|
|
@@ -244,6 +268,8 @@ class Eval:
|
|
|
244
268
|
return (W.c_uint(e) if isinstance(e, int) else str(e), raw.strip())
|
|
245
269
|
if "divide" in spec:
|
|
246
270
|
return (self._ticks(row, raw, spec), "")
|
|
271
|
+
if "scale" in spec:
|
|
272
|
+
return (self._scaled(row, raw, spec), "")
|
|
247
273
|
if fmt == "int": # a numeric value held in a STRING param (e.g. ComSignalInitValue)
|
|
248
274
|
n = parse_int(raw.strip())
|
|
249
275
|
if n is None:
|
|
@@ -261,6 +287,20 @@ class Eval:
|
|
|
261
287
|
raise BindingError(f"{row.path}: cannot convert {spec.get('name')} = {raw} to ticks of {spec['divide']} = {base}")
|
|
262
288
|
return W.c_uint(int(round(v / b)))
|
|
263
289
|
|
|
290
|
+
def _scaled(self, row: Container, raw, spec: dict) -> str:
|
|
291
|
+
"""value * scale as an integer (e.g. LinIfDelay seconds -> ms); a value that is not a whole multiple is
|
|
292
|
+
an error, never rounded silently."""
|
|
293
|
+
from ..values import parse_float
|
|
294
|
+
v = parse_float(str(raw).strip())
|
|
295
|
+
if v is None:
|
|
296
|
+
raise BindingError(f"{row.path}: {spec.get('name')} = {raw} is not a number")
|
|
297
|
+
x = v * spec["scale"]
|
|
298
|
+
n = int(round(x))
|
|
299
|
+
if abs(x - n) > 1e-6 * max(1.0, abs(x)):
|
|
300
|
+
raise BindingError(f"{row.path}: {spec['path']} = {str(raw).strip()} is not a whole multiple of "
|
|
301
|
+
f"1/{spec['scale']}; the static code's {spec.get('name')} field holds an integer")
|
|
302
|
+
return W.c_uint(n)
|
|
303
|
+
|
|
264
304
|
def _agg(self, row: Container, spec: dict) -> tuple:
|
|
265
305
|
"""Aggregate params over the targets of a multi-valued reference of the row (e.g. the smallest non-zero
|
|
266
306
|
timeout of an I-PDU's signals, SWS_Com_00291)."""
|
|
@@ -373,6 +373,124 @@
|
|
|
373
373
|
"init": ["Com_TxSignalInfo", "Com_RxSignalInfo", "Com_TxIPduInfo", "Com_RxIPduInfo"]
|
|
374
374
|
}
|
|
375
375
|
]
|
|
376
|
+
},
|
|
377
|
+
"LinIf": {
|
|
378
|
+
"detect": {"header": "LinIf.h", "contains": "LinIf_FrameType"},
|
|
379
|
+
"sourceIncludes": ["LinIf.h"],
|
|
380
|
+
"sets": {"TxPdus": {"rows": "LinIfTxPdu"}},
|
|
381
|
+
"handleLayout": [{"param": "LinIfTxPduId", "sets": ["TxPdus"]}],
|
|
382
|
+
"require": [
|
|
383
|
+
{
|
|
384
|
+
"rows": "LinIfFrame",
|
|
385
|
+
"path": "param:LinIfFrameType",
|
|
386
|
+
"in": ["UNCONDITIONAL"],
|
|
387
|
+
"why": "LinIf_FrameType has no frame type: LinIf.c sends every slot as an unconditional frame (no sporadic, event-triggered, diagnostic MRF/SRF or node configuration frames)"
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
"rows": "LinIfFrame",
|
|
391
|
+
"path": ["sub:LinIfPduDirection", "child", "def"],
|
|
392
|
+
"in": ["LinIfTxPdu", "LinIfRxPdu"],
|
|
393
|
+
"why": "LinIf_FrameType.Direction is LINIF_DIR_TX or LINIF_DIR_RX only (no slave-to-slave or internal PDUs)"
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
"rows": "LinIfScheduleTable",
|
|
397
|
+
"path": "param:LinIfRunMode",
|
|
398
|
+
"in": ["RUN_CONTINUOUS"],
|
|
399
|
+
"why": "LinIf_ScheduleType has no run mode: LinIf_MainFunction cycles its entries continuously"
|
|
400
|
+
}
|
|
401
|
+
],
|
|
402
|
+
"unsupported": [
|
|
403
|
+
{
|
|
404
|
+
"rows": "LinIfChannel",
|
|
405
|
+
"max": 1,
|
|
406
|
+
"why": "LinIf_ConfigType has one frame and one schedule list and no channel field"
|
|
407
|
+
},
|
|
408
|
+
{"rows": "LinIfSlave", "why": "LinIf.h is a master-node LinIf (LinIf_MainFunction runs the schedule)"},
|
|
409
|
+
{"rows": "LinIfSubstitutionFrames", "why": "LinIf_FrameType has no substitution frame list"},
|
|
410
|
+
{"rows": "LinIfFixedFrameSdu", "why": "LinIf_FrameType has no fixed response SDU"}
|
|
411
|
+
],
|
|
412
|
+
"macros": [
|
|
413
|
+
{"name": "LINIF_NUMBER_OF_CHANNELS", "value": "count:LinIfChannel"},
|
|
414
|
+
{"name": "LINIF_NUMBER_OF_TX_PDUS", "value": "count:LinIfTxPdu"},
|
|
415
|
+
{"name": "LINIF_NUMBER_OF_FRAMES", "value": "count:LinIfFrame"},
|
|
416
|
+
{"name": "LINIF_NUMBER_OF_SCHEDULES", "value": "count:LinIfScheduleTable"}
|
|
417
|
+
],
|
|
418
|
+
"tables": [
|
|
419
|
+
{
|
|
420
|
+
"name": "LinIf_Frames",
|
|
421
|
+
"type": "LinIf_FrameType",
|
|
422
|
+
"qualifier": "LINIF_CONST",
|
|
423
|
+
"static": true,
|
|
424
|
+
"rows": "LinIfFrame",
|
|
425
|
+
"size": "LINIF_NUMBER_OF_FRAMES",
|
|
426
|
+
"doc": "LinIfFrame in configuration order; LinIf_EntryType.FrameIndex indexes this table",
|
|
427
|
+
"fields": [
|
|
428
|
+
{"name": "FrameId", "path": "param:LinIfFrameId", "fmt": "hex"},
|
|
429
|
+
{
|
|
430
|
+
"name": "Length",
|
|
431
|
+
"path": [
|
|
432
|
+
["sub:LinIfPduDirection", "sub:LinIfTxPdu", "ref:LinIfTxPduRef", "param:PduLength"],
|
|
433
|
+
["sub:LinIfPduDirection", "sub:LinIfRxPdu", "ref:LinIfRxPduRef", "param:PduLength"]
|
|
434
|
+
]
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"name": "Checksum",
|
|
438
|
+
"path": "param:LinIfChecksumType",
|
|
439
|
+
"enum": {"CLASSIC": "LINIF_CHECKSUM_CLASSIC", "ENHANCED": "LINIF_CHECKSUM_ENHANCED"}
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
"name": "Direction",
|
|
443
|
+
"path": ["sub:LinIfPduDirection", "child", "def"],
|
|
444
|
+
"enum": {"LinIfTxPdu": "LINIF_DIR_TX", "LinIfRxPdu": "LINIF_DIR_RX"}
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
"name": "PduRPduId",
|
|
448
|
+
"path": [
|
|
449
|
+
["sub:LinIfPduDirection", "sub:LinIfTxPdu", "peer:LinIfTxPduRef@PduR/PduRDestPdu", "sym"],
|
|
450
|
+
["sub:LinIfPduDirection", "sub:LinIfRxPdu", "peer:LinIfRxPduRef@PduR/PduRSrcPdu", "sym"]
|
|
451
|
+
]
|
|
452
|
+
}
|
|
453
|
+
]
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
"name": "LinIf_Entries_{}",
|
|
457
|
+
"type": "LinIf_EntryType",
|
|
458
|
+
"qualifier": "LINIF_CONST",
|
|
459
|
+
"static": true,
|
|
460
|
+
"per": "LinIfScheduleTable",
|
|
461
|
+
"rows": "LinIfEntry",
|
|
462
|
+
"order": "LinIfEntryIndex",
|
|
463
|
+
"contiguous": true,
|
|
464
|
+
"doc": "{} slots in LinIfEntryIndex order (ECUC_LinIf_00011: position in the schedule table)",
|
|
465
|
+
"fields": [
|
|
466
|
+
{"name": "FrameIndex", "path": ["ref:LinIfFrameRef", "index"]},
|
|
467
|
+
{"name": "DelayMs", "path": "param:LinIfDelay", "scale": 1000}
|
|
468
|
+
]
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
"name": "LinIf_Schedules",
|
|
472
|
+
"type": "LinIf_ScheduleType",
|
|
473
|
+
"qualifier": "LINIF_CONST",
|
|
474
|
+
"static": true,
|
|
475
|
+
"rows": "LinIfScheduleTable",
|
|
476
|
+
"size": "LINIF_NUMBER_OF_SCHEDULES",
|
|
477
|
+
"designated": true,
|
|
478
|
+
"doc": "indexed by LinIfScheduleTableIndex",
|
|
479
|
+
"comment": "name",
|
|
480
|
+
"fields": [
|
|
481
|
+
{"name": "Entries", "path": "name", "fmt": "LinIf_Entries_{}"},
|
|
482
|
+
{"name": "NumEntries", "path": "count:LinIfEntry"}
|
|
483
|
+
]
|
|
484
|
+
}
|
|
485
|
+
],
|
|
486
|
+
"objects": [
|
|
487
|
+
{
|
|
488
|
+
"name": "LinIf_Config",
|
|
489
|
+
"type": "LinIf_ConfigType",
|
|
490
|
+
"qualifier": "LINIF_CONST",
|
|
491
|
+
"init": ["LinIf_Frames", "LinIf_Schedules", "LINIF_NUMBER_OF_FRAMES", "LINIF_NUMBER_OF_SCHEDULES"]
|
|
492
|
+
}
|
|
493
|
+
]
|
|
376
494
|
}
|
|
377
495
|
}
|
|
378
496
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""LinIf: LinIf_Cfg.h / LinIf_PBcfg.c.
|
|
2
|
+
|
|
3
|
+
Def-driven (generic emitter): the LinIfGeneral switches and the TPS_ECUC_02108 / SWS_BSW_00200 symbols
|
|
4
|
+
LinIfConf_LinIfTxPdu_<Pdu> (LinIfTxPduId, the handle PduR passes to LinIf_Transmit) and
|
|
5
|
+
LinIfConf_LinIfScheduleTable_<Table> (LinIfScheduleTableIndex, used with LinIf_ScheduleRequest).
|
|
6
|
+
|
|
7
|
+
The static LinIf's own layout (frame table, one entry list per schedule table, schedule table list,
|
|
8
|
+
root config object) comes from its binding (bindings/*.json): channels, frames with their EcuC PDU
|
|
9
|
+
length and PduR handle (via the shared EcucPduCollection/Pdu), schedule entries in LinIfEntryIndex
|
|
10
|
+
order with LinIfDelay, schedule tables by LinIfScheduleTableIndex. Configuration the binding cannot
|
|
11
|
+
represent (non-unconditional frames, slave nodes, more channels than the static code has, ...) is
|
|
12
|
+
listed in the binding's `require` / `unsupported` and fails generation.
|
|
13
|
+
|
|
14
|
+
This module adds only the SWS_LinIf consistency rules that hold for any static code:
|
|
15
|
+
* a schedule entry (LinIfEntry/LinIfFrameRef) must reference a frame of the schedule table's own
|
|
16
|
+
channel (ECUC_LinIf_00016; schedule tables are per channel, ECUC_LinIf_00365);
|
|
17
|
+
* the LinIfFrameId of the unconditional frames of a channel must be unique (one publisher per
|
|
18
|
+
frame identifier on a LIN cluster).
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from ..registry import Generator, register
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _channel(c):
|
|
26
|
+
while c is not None and c.def_name != "LinIfChannel":
|
|
27
|
+
c = c.parent
|
|
28
|
+
return c
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@register("LinIf")
|
|
32
|
+
class LinIf(Generator):
|
|
33
|
+
std_header = "ComStack_Types.h"
|
|
34
|
+
|
|
35
|
+
def specific(self, mo):
|
|
36
|
+
for e in self.cg.instances(self.mc, "LinIfEntry"):
|
|
37
|
+
fr = self.cg.target(e.value("LinIfFrameRef"))
|
|
38
|
+
if fr is None:
|
|
39
|
+
mo.errors.append(f"{e.path}: LinIfFrameRef is not set or does not resolve to a LinIfFrame")
|
|
40
|
+
elif _channel(fr) is not _channel(e):
|
|
41
|
+
mo.errors.append(f"{e.path}: LinIfFrameRef points to {fr.path}, a frame of another channel "
|
|
42
|
+
f"(schedule tables are per channel, ECUC_LinIf_00365)")
|
|
43
|
+
for ch in self.cg.instances(self.mc, "LinIfChannel"):
|
|
44
|
+
seen: dict = {}
|
|
45
|
+
for fr in ch.sub("LinIfFrame"):
|
|
46
|
+
if (fr.text("LinIfFrameType") or "").strip() != "UNCONDITIONAL":
|
|
47
|
+
continue
|
|
48
|
+
fid = (fr.text("LinIfFrameId") or "").strip()
|
|
49
|
+
if fid in seen:
|
|
50
|
+
mo.errors.append(f"{fr.path}: LinIfFrameId {fid} is also used by {seen[fid]} on channel "
|
|
51
|
+
f"{ch.name}; unconditional frame identifiers must be unique per channel")
|
|
52
|
+
else:
|
|
53
|
+
seen[fid] = fr.name
|
|
@@ -98,10 +98,11 @@ def apply_binding(cg, mo: E.ModuleOutput, mb: dict, ext: Optional[dict] = None):
|
|
|
98
98
|
f"{', '.join('(not configured)' if x is None else str(x) for x in req['in'])}: {req.get('why', '')}")
|
|
99
99
|
for un in mb.get("unsupported", []):
|
|
100
100
|
rows = cg.instances(mo.mc, un["rows"])
|
|
101
|
-
|
|
101
|
+
mx = un.get("max", 0)
|
|
102
|
+
if len(rows) > mx:
|
|
102
103
|
mo.errors.append(f"{len(rows)} {un['rows']} container(s) configured ({', '.join(r.name for r in rows[:5])}"
|
|
103
104
|
f"{', ...' if len(rows) > 5 else ''}), which the {mb['_binding']} static code cannot "
|
|
104
|
-
f"represent: {un.get('why', '')}")
|
|
105
|
+
f"represent{f' (at most {mx})' if mx else ''}: {un.get('why', '')}")
|
|
105
106
|
for inc in mb.get("sourceIncludes", []):
|
|
106
107
|
mo.include(inc)
|
|
107
108
|
macros = []
|
|
@@ -121,41 +122,9 @@ def apply_binding(cg, mo: E.ModuleOutput, mb: dict, ext: Optional[dict] = None):
|
|
|
121
122
|
name = vs["name"].replace("{}", r.name)
|
|
122
123
|
lines.append(f"{'static ' if vs.get('static') else ''}VAR({vs['type']}, {vs['qualifier']}) {name}[{size}];")
|
|
123
124
|
pending.append(("var", ev.rank, vs.get("section", "VAR_NOINIT_UNSPECIFIED"), "\n".join(lines)))
|
|
124
|
-
for
|
|
125
|
-
ev
|
|
126
|
-
|
|
127
|
-
if t.get("order"):
|
|
128
|
-
from ..values import parse_int
|
|
129
|
-
rows.sort(key=lambda r: (parse_int(r.text(t["order"]) or "") is None, parse_int(r.text(t["order"]) or "") or 0))
|
|
130
|
-
lines, externs = [], []
|
|
131
|
-
des = t.get("designated")
|
|
132
|
-
if des:
|
|
133
|
-
_check_index(cg, mo, t, rows, des, values)
|
|
134
|
-
for r in rows:
|
|
135
|
-
vals = []
|
|
136
|
-
for f in t["fields"]:
|
|
137
|
-
v, _ = ev.field(r, f)
|
|
138
|
-
vals.append(v)
|
|
139
|
-
if f.get("extern") and v not in ("NULL_PTR", "0", "0u") and not v.startswith('"'):
|
|
140
|
-
m = re.search(r"[A-Za-z_]\w*", v)
|
|
141
|
-
if m:
|
|
142
|
-
ext_ = "extern " + f["extern"].replace("{}", m.group(0)) + ";"
|
|
143
|
-
if ext_ not in externs:
|
|
144
|
-
externs.append(ext_)
|
|
145
|
-
lead = ""
|
|
146
|
-
if des:
|
|
147
|
-
lead = f"[{des.replace('{sym}', cg.symbol(r)) if isinstance(des, str) else cg.symbol(r)}] = "
|
|
148
|
-
comment = t.get("comment", "name")
|
|
149
|
-
cm = {"name": r.name, "sym": cg.symbol(r) or r.name}.get(comment, r.name)
|
|
150
|
-
if t.get("scalar"):
|
|
151
|
-
lines.append(f" {lead}{vals[0]} /* {cm} */")
|
|
152
|
-
else:
|
|
153
|
-
lines.append(f" {lead}{{ {', '.join(vals)} }} /* {cm} */")
|
|
154
|
-
st = "static " if t.get("static") else ""
|
|
155
|
-
size = t.get("size", "")
|
|
156
|
-
body = (f"/* {t.get('doc', t['rows'])} */\n{st}CONST({t['type']}, {t['qualifier']}) {t['name']}[{size}] = {{\n" +
|
|
157
|
-
",\n".join(lines) + "\n};")
|
|
158
|
-
pending.append(("table", ev.rank, body, externs))
|
|
125
|
+
for t0 in mb.get("tables", []):
|
|
126
|
+
for t, rows in _table_instances(cg, mo, ev, t0, sets):
|
|
127
|
+
_emit_table(cg, mo, ev, t, rows, values, pending)
|
|
159
128
|
objects = mb.get("objects", [])
|
|
160
129
|
if objects:
|
|
161
130
|
# a root config object points at the (static) tables and buffers, so they all live in its file:
|
|
@@ -173,6 +142,67 @@ def apply_binding(cg, mo: E.ModuleOutput, mb: dict, ext: Optional[dict] = None):
|
|
|
173
142
|
mo.used_mips |= ev.used_mips
|
|
174
143
|
|
|
175
144
|
|
|
145
|
+
def _table_instances(cg, mo, ev, t: dict, sets: dict) -> list:
|
|
146
|
+
"""[(table spec, rows)]: one table, or one per parent instance for a "per" table."""
|
|
147
|
+
from ..values import parse_int
|
|
148
|
+
num = lambda r: parse_int(r.text(t["order"]) or "") # noqa: E731
|
|
149
|
+
out = []
|
|
150
|
+
if t.get("per"):
|
|
151
|
+
specs = []
|
|
152
|
+
for par in cg.sorted_instances(mo.mc, t["per"]):
|
|
153
|
+
rows = par.sub(t["rows"])
|
|
154
|
+
if not rows:
|
|
155
|
+
raise B.BindingError(f"{par.path}: no {t['rows']} configured; {t['name'].replace('{}', par.name)} "
|
|
156
|
+
"would be an empty array, which C does not allow")
|
|
157
|
+
specs.append((dict(t, name=t["name"].replace("{}", par.name),
|
|
158
|
+
doc=t.get("doc", t["rows"]).replace("{}", par.name)), rows))
|
|
159
|
+
else:
|
|
160
|
+
specs = [(t, ev.rows(t["rows"], sets))]
|
|
161
|
+
for spec, rows in specs:
|
|
162
|
+
if t.get("order"):
|
|
163
|
+
rows = sorted(rows, key=lambda r: (num(r) is None, num(r) or 0))
|
|
164
|
+
if t.get("contiguous"):
|
|
165
|
+
got = [num(r) for r in rows]
|
|
166
|
+
if got != list(range(len(rows))):
|
|
167
|
+
raise B.BindingError(f"{spec['name']}: {t['order']} of the {len(rows)} {t['rows']} rows must be exactly "
|
|
168
|
+
f"0..{len(rows) - 1} (table position), got {', '.join(map(str, got))}")
|
|
169
|
+
out.append((spec, rows))
|
|
170
|
+
return out
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _emit_table(cg, mo, ev, t: dict, rows: list, values: dict, pending: list):
|
|
174
|
+
ev.rank = 1
|
|
175
|
+
lines, externs = [], []
|
|
176
|
+
des = t.get("designated")
|
|
177
|
+
if des:
|
|
178
|
+
_check_index(cg, mo, t, rows, des, values)
|
|
179
|
+
for r in rows:
|
|
180
|
+
vals = []
|
|
181
|
+
for f in t["fields"]:
|
|
182
|
+
v, _ = ev.field(r, f)
|
|
183
|
+
vals.append(v)
|
|
184
|
+
if f.get("extern") and v not in ("NULL_PTR", "0", "0u") and not v.startswith('"'):
|
|
185
|
+
m = re.search(r"[A-Za-z_]\w*", v)
|
|
186
|
+
if m:
|
|
187
|
+
ext_ = "extern " + f["extern"].replace("{}", m.group(0)) + ";"
|
|
188
|
+
if ext_ not in externs:
|
|
189
|
+
externs.append(ext_)
|
|
190
|
+
lead = ""
|
|
191
|
+
if des:
|
|
192
|
+
lead = f"[{des.replace('{sym}', cg.symbol(r)) if isinstance(des, str) else cg.symbol(r)}] = "
|
|
193
|
+
comment = t.get("comment", "name")
|
|
194
|
+
cm = {"name": r.name, "sym": cg.symbol(r) or r.name}.get(comment, r.name)
|
|
195
|
+
if t.get("scalar"):
|
|
196
|
+
lines.append(f" {lead}{vals[0]} /* {cm} */")
|
|
197
|
+
else:
|
|
198
|
+
lines.append(f" {lead}{{ {', '.join(vals)} }} /* {cm} */")
|
|
199
|
+
st = "static " if t.get("static") else ""
|
|
200
|
+
size = t.get("size", "")
|
|
201
|
+
body = (f"/* {t.get('doc', t['rows'])} */\n{st}CONST({t['type']}, {t['qualifier']}) {t['name']}[{size}] = {{\n" +
|
|
202
|
+
",\n".join(lines) + "\n};")
|
|
203
|
+
pending.append(("table", ev.rank, body, externs))
|
|
204
|
+
|
|
205
|
+
|
|
176
206
|
def _check_index(cg, mo, t: dict, rows: list, des, values: dict):
|
|
177
207
|
"""A designated table is indexed by handle (minus an offset): every row must land in [0, size) exactly once,
|
|
178
208
|
or the static code's table lookup would be wrong. Reported as an error, never renumbered here."""
|
|
@@ -37,12 +37,30 @@ ComUserCallback of type COM_RX_ACK / COM_TX_ACK; `call:comUserCallback`), or, fo
|
|
|
37
37
|
callbacks, the RTE's `Rte_COMCbk_<sn>()` / `Rte_COMCbkTAck_<sn>()` when the RTE's Rte_Cbk.h declares it
|
|
38
38
|
(`call:rteSignalCallback`). Nothing is bound from guesses: no declaration, no callback.
|
|
39
39
|
|
|
40
|
+
## LinIf
|
|
41
|
+
`LinIf_Cfg.h`: LinIfGeneral switches, `LinIfConf_LinIfTxPdu_*` (LinIfTxPduId, the handle PduR passes to `LinIf_Transmit`) and
|
|
42
|
+
`LinIfConf_LinIfScheduleTable_*` (LinIfScheduleTableIndex) symbols (TPS_ECUC_02108 / SWS_BSW_00200), `LINIF_NUMBER_OF_*`.
|
|
43
|
+
`LinIf_PBcfg.c` (binding): frame table in configuration order (LinIfFrameId, length from the EcuC Pdu, checksum, direction
|
|
44
|
+
from the LinIfPduDirection choice, PduR dest (Tx) / src (Rx) handle via the shared EcuC Pdu), one entry list per schedule
|
|
45
|
+
table (`per` table, LinIfEntryIndex order, which must be 0..n-1; FrameIndex = position of LinIfFrameRef's frame;
|
|
46
|
+
LinIfDelay s -> ms, a non-whole ms is an error), schedule list indexed by LinIfScheduleTableIndex, root `LinIf_Config`.
|
|
47
|
+
Tx PDU handles are laid out 0..n-1 (handleLayout; `LinIf_Transmit` range-checks them).
|
|
48
|
+
Errors, never dropped: frame types other than UNCONDITIONAL, PDU directions other than Tx/Rx, RUN_ONCE schedules, more
|
|
49
|
+
than one channel, slave nodes, substitution frames, fixed frame SDUs (stand-in binding); an entry whose frame belongs to
|
|
50
|
+
another channel, duplicate unconditional LinIfFrameIds on a channel (any binding).
|
|
51
|
+
|
|
52
|
+
Binding path/table additions used by LinIf: `child` (chosen sub-container of a choice), `def` (its def name, mapped with
|
|
53
|
+
`enum`), `count:<Def>`, field `scale`, table `per` / `contiguous`, `unsupported[].max`.
|
|
54
|
+
|
|
40
55
|
## Known differences from the sample/large fixture oracle (all explained)
|
|
41
56
|
| Item | Oracle | Ours | Why |
|
|
42
57
|
|---|---|---|---|
|
|
43
58
|
| `NvM_BlockDescriptorTable[*].RamBlockDataAddress` | `X` | `&X` (as configured) | ECUC_NvM_00482 value is the address expression; kept verbatim, same address for arrays |
|
|
44
59
|
| Rx I-PDU `TimeoutTicks` with mixed signal timeouts | largest ComTimeout | smallest non-zero | SWS_Com_00291 |
|
|
45
60
|
| `<MIP>_CFG_*` version macros, generic ComGeneral/NvMCommon/... switches, Dem/Fee tables | absent | extra | def-driven output the oracle does not emit |
|
|
61
|
+
| LinIf entry list name | `LinIf_Sched_<Table>` | `LinIf_Entries_<Table>` | `static` table, internal linkage: not part of the LinIf.h contract |
|
|
62
|
+
| `LinSMConf_LinSMSchedule_*` in `LinIf_Cfg.h` | present | absent (in `LinSM_Cfg.h`) | a `LinSMConf_` symbol belongs to the LinSM config header (SWS_BSW_00200) |
|
|
63
|
+
| `LINIF_NUMBER_OF_SCHEDULES`, LinIfGeneral switches, version macros | absent | extra | def/binding-driven output the oracle does not emit |
|
|
46
64
|
| SchM main function names | `Com_MainFunctionTx` | same | SWS_Com says `Com_MainFunctionTx_<shortName>`; the instance named like its def keeps the SIP's legacy name |
|
|
47
65
|
|
|
48
66
|
## Reading a diff
|