Design Decisions

Informative

The rationale behind the cross-module rules in Requirements. This helps implementers and integrators understand why the contract looks the way it does; it is not itself a requirement. Each entry points to the requirement it explains. Per-module rationale lives in each module’s own design-decision page.

Why do some modules have no upper-layer interface layer?

Governs CHI-GEN-MUST-04, CHI-GEN-MUST-14.

Can, Lin, Mem and Wdg sit below an established interface stack (CanIf, LinIf, MemIf, WdgIf) that genuinely exists as a separate software component: it multiplexes several drivers, holds state of its own, and is specified elsewhere. Modelling an interface layer above those drivers describes something real.

For I2c, Mcu, Port, Spi and Uart there is no such component. Nothing standard sits between the driver and whatever the integrator happens to build on top, so an <Ma>If layer would be a layer with no implementation, no owner, and no other user — the integrator would end up writing the “interface” themselves anyway, just under a name that suggests a stack they do not have. The alternative that was rejected was to keep the <Ma>If naming for these modules purely for symmetry; that keeps the documentation tidy at the cost of misleading every integrator about what they are expected to supply.

So these five modules declare callouts instead: functions the driver calls, declared in the module’s own header, implemented by the integrator in a generated stub file. The same information crosses the same boundary; only the fiction of a stack is gone.

Implication for implementers: do not ship an <Ma>If.h for these modules. Declare the callout prototypes in <Mip>.h and publish the specification’s types in <Ma>_DriverTypes.h.

Why keep the interface layer for Can, Lin, Mem and Wdg?

Relates to CHI-GEN-MUST-04, CHI-GEN-MUST-15.

Because for those modules the layer is not a fiction. The consequence — accepted deliberately — is that the specification carries two models side by side, and that is a permanent state rather than a migration in progress. The rule for deciding which applies is a property of the module: does an upper-layer interface stack exist for it? Nothing about a project or a delivery changes the answer.

Implication for implementers: read the module’s own specification to learn which model it uses; do not infer it from another module.

Why are callouts named <Mip>_Callout_On<Event>?

Governs CHI-GEN-MUST-15.

Three properties were wanted at once. A callout is an ordinary global function of the module, so it should follow the ordinary function scheme <Mip>_<Fn> (CHI-GEN-MUST-02) — this also makes CHI-GEN-MUST-14 natural, since the prototype belongs in <Mip>.h. The On<Event> vocabulary is worth preserving across modules: an integrator who has seen CanIf_OnReception should recognise Uart_Callout_OnReception immediately. And, decisively, a reader must be able to tell who implements a function from its name.

The plain composition <Mip>_On<Event> satisfied the first two but failed the third. Driver services and integrator callouts would have shared one prefix, so I2c_OnReception would sit in the same namespace as I2c_StartRxTransaction with nothing but the documentation to say that opposite parties implement them. Worse, some modules already provide an inbound notification service — a function the integration code calls to tell the driver about something the hardware cannot report itself, such as Spi_OnCs for an externally detected chip-select edge, or Mem_OnError for an ECC fault from the system handler. Those wear On<Event> names while pointing in the opposite direction to a callout, so the name would have been actively misleading.

Reserving a Callout infix resolves all three: <Mip>_Callout_On<Event> is recognisably a module function, recognisably an On<Event> notification, and unmistakably the integrator’s to implement. It also settles the inbound services without renaming them — they simply lack the infix.

Implication for implementers: put Callout directly after the module prefix; keep the event half of the name identical to the event name other modules use for the same kind of event; and do not give a driver service a name containing _Callout_.

Why is the callout code generated into its own file?

Governs CHI-GEN-MUST-16.

The integrator needs a definite place to write code, and the generator needs to be able to run again without destroying it. A single generated file per module with explicit user-code blocks satisfies both: the integrator never has to create a file or work out where a callout is expected, and the generator owns everything outside the blocks. Emitting one default implementation per callout also means a project links successfully before any integration code is written.

Callouts that the active configuration does not enable are still emitted, excluded from compilation. Dropping them instead would silently delete the integrator’s code whenever an event is temporarily disabled.

Implication for implementers: generate <Mip>_Callout_Stubs.c for every module that defines callouts, including the disabled ones, and never rewrite the contents of a user-code block.

Why do the user-code markers carry the full callout name?

Relates to CHI-GEN-MUST-16.

The marker has to identify which callout a block of user code belongs to. Naming the callout in full matches the convention integrators already know from comparable generated stub files, and it makes the file readable on its own — the marker says exactly which function’s body you are looking at.

The alternative considered was to key the marker on the event name only (<USERBLOCK OnReception>), which would survive a future renaming of the callout scheme. It was rejected in favour of the more familiar and more self-describing form. The accepted consequence: if a module specification ever renames a callout, the marker text changes with it, so a regeneration would no longer match user code written against the old name. Such a rename therefore has to be accompanied by a migration of existing stub files — it is not a specification-only change.

Implication for implementers: derive the marker from the callout symbol, and — if you ever rename a callout — ship a migration for the stub files in the field, or accept the old marker as an alias when reading.

Where the naming rule is defined

Relates to CHI-GEN-MUST-15.

The callout naming scheme is stated in exactly one normative place, CHI-GEN-MUST-15, and illustrated in exactly one informative place, Naming Conventions. Module pages cite those rather than restating the rule, and the cross-references in each module’s API reference are anchored on the event rather than on the full symbol. This is deliberate: it keeps the scheme changeable later at the cost of one requirement, one convention page, and a mechanical symbol sweep, instead of a rewrite of every module’s prose.

Implication for implementers: when documenting a module, link to the convention instead of repeating it.