@typecad/cuttlefish 1.0.0-alpha.14 → 1.0.0-alpha.16
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 +82 -79
- package/assets/editor-extensions/typecad-debug/README.md +25 -26
- package/dist/api/config.d.ts +22 -26
- package/dist/api/schema/types.d.ts +20 -3
- package/dist/api/shared/async-runtime-static.d.ts +1 -1
- package/dist/api/shared/async-runtime-static.js +5 -5
- package/dist/api/shared/async-types.d.ts +3 -2
- package/dist/api/shared/board-catalog.d.ts +2 -0
- package/dist/api/shared/board-catalog.js +6 -0
- package/dist/api/shared/board-resolver.d.ts +4 -7
- package/dist/api/shared/board-resolver.js +2 -1
- package/dist/api/shared/coop-scheduler.d.ts +1 -1
- package/dist/api/shared/coop-scheduler.js +1 -1
- package/dist/api/shared/cpp-type-ir.d.ts +1 -1
- package/dist/api/shared/cpp-type-ir.js +1 -1
- package/dist/api/shared/display-profile.d.ts +1 -1
- package/dist/api/shared/display-profile.js +3 -5
- package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
- package/dist/api/shared/framework-manifest-registry.js +0 -1
- package/dist/api/shared/framework-manifest.d.ts +7 -6
- package/dist/api/shared/framework-manifest.js +6 -11
- package/dist/api/shared/hal-op-ir.d.ts +294 -685
- package/dist/api/shared/hal-op-ir.js +31 -65
- package/dist/api/shared/index.d.ts +0 -4
- package/dist/api/shared/index.js +1 -7
- package/dist/api/shared/ir-core.d.ts +6 -0
- package/dist/api/shared/ir-declarations.d.ts +4 -0
- package/dist/api/shared/ir.d.ts +1 -3
- package/dist/api/shared/platform-strategy.d.ts +14 -37
- package/dist/api/shared/polyfill-helper-registry.js +0 -5
- package/dist/api/shared/polyfill-types.js +0 -27
- package/dist/api/shared/promise-runtime.js +3 -3
- package/dist/api/shared/spdx-licenses.d.ts +12 -10
- package/dist/api/shared/spdx-licenses.js +1 -1
- package/dist/api/shared/toolchain-types.d.ts +1 -1
- package/dist/api/shared/toolchain-types.js +3 -3
- package/dist/api/shared/types.d.ts +0 -3
- package/dist/api/shared/validate-framework-manifest.js +13 -33
- package/dist/board-catalog/dts-reader.d.ts +220 -0
- package/dist/board-catalog/dts-reader.js +1052 -0
- package/dist/board-catalog/index.d.ts +11 -0
- package/dist/board-catalog/index.js +12 -0
- package/dist/board-catalog/pinconfig.d.ts +48 -0
- package/dist/board-catalog/pinconfig.js +263 -0
- package/dist/board-catalog/sdk.d.ts +82 -0
- package/dist/board-catalog/sdk.js +180 -0
- package/dist/board-catalog/store.d.ts +196 -0
- package/dist/board-catalog/store.js +363 -0
- package/dist/board-catalog/types.d.ts +233 -0
- package/dist/board-catalog/types.js +7 -0
- package/dist/board-catalog/walker.d.ts +112 -0
- package/dist/board-catalog/walker.js +1101 -0
- package/dist/cli-utils.d.ts +2 -2
- package/dist/cli-utils.js +5 -5
- package/dist/cli.js +219 -242
- package/dist/config-loader.d.ts +14 -19
- package/dist/config-loader.js +240 -78
- package/dist/config-schema.d.ts +3 -5
- package/dist/config-schema.js +8 -8
- package/dist/contract/board-generator.d.ts +4 -4
- package/dist/contract/board-generator.js +16 -5
- package/dist/contract/contract-parser.d.ts +27 -0
- package/dist/contract/contract-parser.js +76 -0
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.js +70 -21
- package/dist/create/active-board-catalog.d.ts +9 -0
- package/dist/create/active-board-catalog.js +94 -0
- package/dist/create/board-catalog.generated.d.ts +96 -0
- package/dist/create/board-catalog.generated.js +128737 -0
- package/dist/create/board-search.d.ts +23 -0
- package/dist/create/board-search.js +88 -0
- package/dist/create/editor-integration.d.ts +4 -2
- package/dist/create/editor-integration.js +41 -2
- package/dist/create/framework-catalog.d.ts +57 -9
- package/dist/create/framework-catalog.js +76 -57
- package/dist/create/index.d.ts +5 -11
- package/dist/create/index.js +3 -8
- package/dist/create/mcu-target.d.ts +39 -0
- package/dist/create/mcu-target.js +80 -0
- package/dist/create/pack-targets.d.ts +19 -0
- package/dist/create/pack-targets.js +56 -0
- package/dist/create/scaffold.d.ts +4 -7
- package/dist/create/scaffold.js +19 -109
- package/dist/create/templates.d.ts +32 -5
- package/dist/create/templates.js +130 -116
- package/dist/create/wizard.d.ts +12 -1
- package/dist/create/wizard.js +168 -38
- package/dist/debug/preprocessor.d.ts +5 -0
- package/dist/debug/preprocessor.js +31 -7
- package/dist/diagnostics/diagnostics-report.d.ts +1 -1
- package/dist/diagnostics/diagnostics-report.js +3 -11
- package/dist/diagnostics/json-schema.d.ts +1 -1
- package/dist/diagnostics/md-writer.js +2 -2
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +1 -1
- package/dist/emit/cpp-emitter.js +4 -1
- package/dist/emit/emitters/class-emitter.js +32 -4
- package/dist/emit/emitters/emitter-context.d.ts +11 -0
- package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
- package/dist/emit/emitters/function-emitter-impl.js +33 -17
- package/dist/emit/emitters/output-finalizer.d.ts +7 -0
- package/dist/emit/emitters/output-finalizer.js +31 -23
- package/dist/emit/emitters/setup.js +125 -63
- package/dist/emit/emitters/top-level-prep.js +1 -1
- package/dist/emit/emitters/type-decl-emitter.js +13 -8
- package/dist/emit/emitters/ui-emitter.js +1 -1
- package/dist/emit/expression-renderer.d.ts +3 -3
- package/dist/emit/expression-renderer.js +17 -7
- package/dist/emit/route-hal-op.js +17 -48
- package/dist/emit/snprintf-helpers.d.ts +1 -3
- package/dist/emit/snprintf-helpers.js +1 -39
- package/dist/emit/statement-renderer.d.ts +0 -4
- package/dist/emit/statement-renderer.js +54 -45
- package/dist/emit/utils/async-state-machine.d.ts +16 -9
- package/dist/emit/utils/async-state-machine.js +56 -54
- package/dist/emit/utils/comment-helpers.d.ts +0 -8
- package/dist/emit/utils/comment-helpers.js +1 -1
- package/dist/emit/utils/cpp-helpers.d.ts +2 -2
- package/dist/emit/utils/cpp-helpers.js +2 -2
- package/dist/emit/utils/hal-op-cpp-type.js +4 -6
- package/dist/emit/utils/index.d.ts +3 -3
- package/dist/emit/utils/index.js +3 -3
- package/dist/emit/utils/type-inference.d.ts +1 -29
- package/dist/emit/utils/type-inference.js +0 -75
- package/dist/framework-package.d.ts +3 -0
- package/dist/framework-package.js +23 -6
- package/dist/framework-registry.d.ts +5 -5
- package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
- package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
- package/dist/frameworks/native/index.d.ts +3 -0
- package/dist/frameworks/native/index.js +15 -0
- package/dist/frameworks/native/native-compile.d.ts +17 -0
- package/dist/frameworks/native/native-compile.js +170 -0
- package/dist/frameworks/native/native-config.d.ts +41 -0
- package/dist/frameworks/native/native-config.js +7 -0
- package/dist/frameworks/native/strategy.d.ts +97 -0
- package/dist/frameworks/native/strategy.js +664 -0
- package/dist/ir/board-resolver.d.ts +18 -23
- package/dist/ir/board-resolver.js +43 -401
- package/dist/ir/build-ir-state.d.ts +3 -0
- package/dist/ir/build-ir-state.js +42 -0
- package/dist/ir/build-ir.d.ts +1 -1
- package/dist/ir/build-ir.js +40 -42
- package/dist/ir/declaration-builders.js +1 -0
- package/dist/ir/expression-to-ir.js +17 -10
- package/dist/ir/hal/hal-emitter.d.ts +0 -2
- package/dist/ir/hal/hal-emitter.js +92 -41
- package/dist/ir/hal/hal-parser.d.ts +5 -5
- package/dist/ir/hal/hal-parser.js +128 -36
- package/dist/ir/hal/hal-plugins.d.ts +0 -1
- package/dist/ir/hal/hal-plugins.js +425 -716
- package/dist/ir/heap-analysis.js +2 -41
- package/dist/ir/identifier-collector.js +32 -20
- package/dist/ir/network-validation.js +7 -11
- package/dist/ir/ownership-analysis.js +75 -13
- package/dist/ir/peripheral-usage.d.ts +8 -2
- package/dist/ir/peripheral-usage.js +78 -149
- package/dist/ir/peripheral-validation.js +23 -3
- package/dist/ir/pin-capability-validation.d.ts +12 -0
- package/dist/ir/pin-capability-validation.js +61 -97
- package/dist/ir/pin-mode-validation.js +5 -5
- package/dist/ir/program-analysis.d.ts +8 -15
- package/dist/ir/program-analysis.js +140 -323
- package/dist/ir/pulldown-validation.js +11 -4
- package/dist/ir/resource-analysis.js +15 -3
- package/dist/ir/statement-to-ir.js +0 -6
- package/dist/ir/timing-validation.js +1 -1
- package/dist/ir/transformers/array-methods.js +11 -5
- package/dist/ir/transformers/call-statement.js +20 -87
- package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
- package/dist/ir/transformers/callback-context-registry.js +8 -16
- package/dist/ir/transformers/control-flow.js +0 -13
- package/dist/ir/transformers/expressions.js +10 -9
- package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
- package/dist/ir/transformers/hal-call-resolver.js +24 -10
- package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
- package/dist/ir/transformers/hal-emit-helpers.js +2 -2
- package/dist/ir/transformers/namespace-methods.js +1 -1
- package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
- package/dist/ir/transformers/ui-callback-lowering.js +0 -24
- package/dist/ir/transformers/variables.js +338 -42
- package/dist/ir/try-catch-validation.js +4 -3
- package/dist/ir/type-resolution.js +15 -4
- package/dist/ir/validation-orchestrator.js +0 -9
- package/dist/libdef/cpp-to-decl.d.ts +0 -9
- package/dist/libdef/cpp-to-decl.js +0 -72
- package/dist/libdef/registry.d.ts +0 -1
- package/dist/libdef/registry.js +1 -24
- package/dist/library/init.d.ts +1 -1
- package/dist/library/init.js +137 -137
- package/dist/library-packages.d.ts +1 -1
- package/dist/library-packages.js +1 -1
- package/dist/mapping/peripheral-names.js +9 -4
- package/dist/mapping/source-map.d.ts +3 -4
- package/dist/mapping/source-map.js +12 -18
- package/dist/orchestrator/graph-builder.d.ts +3 -3
- package/dist/orchestrator/graph-builder.js +13 -12
- package/dist/orchestrator/type-checker.d.ts +3 -3
- package/dist/orchestrator/type-checker.js +3 -3
- package/dist/platform/async-runtime.js +1 -1
- package/dist/platform/coop-scheduler-runtime.js +2 -2
- package/dist/platform/generic-debug-codegen.d.ts +36 -0
- package/dist/platform/generic-debug-codegen.js +185 -0
- package/dist/platform/generic-strategy.d.ts +3 -4
- package/dist/platform/generic-strategy.js +5 -80
- package/dist/platform/index.js +1 -1
- package/dist/platform/registry.d.ts +2 -2
- package/dist/platform/registry.js +2 -2
- package/dist/safety/asil-decorators.d.ts +32 -0
- package/dist/safety/asil-decorators.js +32 -0
- package/dist/safety/authoring.d.ts +85 -0
- package/dist/safety/authoring.js +61 -0
- package/dist/safety/engine.d.ts +3 -0
- package/dist/safety/engine.js +83 -0
- package/dist/safety/hal/ops.d.ts +48 -0
- package/dist/safety/hal/ops.js +46 -0
- package/dist/safety/iso26262/analyze.d.ts +13 -0
- package/dist/safety/iso26262/analyze.js +29 -0
- package/dist/safety/iso26262/asil.d.ts +10 -0
- package/dist/safety/iso26262/asil.js +22 -0
- package/dist/safety/iso26262/collect.d.ts +14 -0
- package/dist/safety/iso26262/collect.js +137 -0
- package/dist/safety/iso26262/goto-checker.d.ts +10 -0
- package/dist/safety/iso26262/goto-checker.js +72 -0
- package/dist/safety/iso26262/heap-checker.d.ts +7 -0
- package/dist/safety/iso26262/heap-checker.js +57 -0
- package/dist/safety/iso26262/init-checker.d.ts +13 -0
- package/dist/safety/iso26262/init-checker.js +127 -0
- package/dist/safety/iso26262/loop-checker.d.ts +7 -0
- package/dist/safety/iso26262/loop-checker.js +86 -0
- package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
- package/dist/safety/iso26262/recursion-checker.js +134 -0
- package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
- package/dist/safety/iso26262/sidecar-writer.js +13 -0
- package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
- package/dist/safety/passes/pinMode-intercept.js +60 -0
- package/dist/safety/runtime/mode-table.d.ts +5 -0
- package/dist/safety/runtime/mode-table.js +55 -0
- package/dist/safety/runtime/polyfills.d.ts +6 -0
- package/dist/safety/runtime/polyfills.js +12 -0
- package/dist/safety/runtime/result-struct.d.ts +11 -0
- package/dist/safety/runtime/result-struct.js +98 -0
- package/dist/safety/runtime/safe-int.d.ts +33 -0
- package/dist/safety/runtime/safe-int.js +170 -0
- package/dist/safety/runtime/safe-traits.d.ts +4 -0
- package/dist/safety/runtime/safe-traits.js +95 -0
- package/dist/safety/runtime/safe-variable.d.ts +39 -0
- package/dist/safety/runtime/safe-variable.js +226 -0
- package/dist/safety/runtime/vote.d.ts +8 -0
- package/dist/safety/runtime/vote.js +92 -0
- package/dist/safety/runtime/write-verify.d.ts +8 -0
- package/dist/safety/runtime/write-verify.js +71 -0
- package/dist/safety/safe-int-types.d.ts +45 -0
- package/dist/safety/safe-int-types.js +25 -0
- package/dist/safety/safe-variable-types.d.ts +29 -0
- package/dist/safety/safe-variable-types.js +19 -0
- package/dist/safety/safety-bridge.d.ts +5 -6
- package/dist/safety/safety-bridge.js +15 -28
- package/dist/safety/sidecar-bridge.d.ts +1 -3
- package/dist/safety/sidecar-bridge.js +3 -20
- package/dist/safety/specifiers.d.ts +6 -0
- package/dist/safety/specifiers.js +14 -0
- package/dist/safety-hook.d.ts +8 -7
- package/dist/safety-hook.js +12 -13
- package/dist/testing.d.ts +4 -5
- package/dist/testing.js +5 -5
- package/dist/transpile/resolution.d.ts +1 -1
- package/dist/transpile/resolution.js +18 -9
- package/dist/transpile/test-pins.d.ts +18 -0
- package/dist/transpile/test-pins.js +142 -0
- package/dist/transpile.d.ts +1 -2
- package/dist/transpile.js +36 -30
- package/dist/types.d.ts +26 -26
- package/dist/ui-hook.d.ts +1 -1
- package/dist/utils/cli.d.ts +2 -2
- package/dist/utils/cli.js +92 -154
- package/dist/utils/fs.d.ts +3 -4
- package/dist/utils/fs.js +4 -5
- package/dist/utils/strings.d.ts +0 -1
- package/dist/utils/strings.js +0 -8
- package/dist/utils/ui.d.ts +5 -2
- package/dist/utils/ui.js +13 -3
- package/package.json +158 -157
- package/dist/add-preset.d.ts +0 -4
- package/dist/add-preset.js +0 -74
- package/dist/api/shared/async-symbol-detector.d.ts +0 -11
- package/dist/api/shared/async-symbol-detector.js +0 -140
- package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
- package/dist/api/shared/native-display-op-resolver.js +0 -64
- package/dist/create/board-checklist.d.ts +0 -2
- package/dist/create/board-checklist.js +0 -52
- package/dist/create/board-codegen.d.ts +0 -9
- package/dist/create/board-codegen.js +0 -238
- package/dist/create/board-generators.d.ts +0 -12
- package/dist/create/board-generators.js +0 -651
- package/dist/create/board-spec.d.ts +0 -181
- package/dist/create/board-spec.js +0 -214
- package/dist/install/framework-catalog.d.ts +0 -53
- package/dist/install/framework-catalog.js +0 -107
- package/dist/install/handle-install.d.ts +0 -35
- package/dist/install/handle-install.js +0 -177
- package/dist/install/index.d.ts +0 -4
- package/dist/install/index.js +0 -3
- package/dist/ir/pin-state-tracking.d.ts +0 -58
- package/dist/ir/pin-state-tracking.js +0 -182
- package/dist/ir/pwm-timer-sharing.d.ts +0 -4
- package/dist/ir/pwm-timer-sharing.js +0 -94
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
- package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
- package/dist/libdef/component-decls.d.ts +0 -2
- package/dist/libdef/component-decls.js +0 -6
- package/dist/libdef/component-discovery.d.ts +0 -43
- package/dist/libdef/component-discovery.js +0 -83
- package/dist/theme-tokens.d.ts +0 -22
- package/dist/theme-tokens.js +0 -172
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { RuntimePolyfillIR } from "../../api/index.js";
|
|
2
|
+
/** SafeInt<T> polyfill: chainable bounds-checked signed-integer arithmetic.
|
|
3
|
+
*
|
|
4
|
+
* Mutating chain — every math op returns SafeInt<T>& so calls compose:
|
|
5
|
+
* x.add(5).mul(2).get()
|
|
6
|
+
*
|
|
7
|
+
* Sticky-fault overflow policy: on overflow or division error, set fault_
|
|
8
|
+
* and stop computing. Subsequent ops are no-ops returning *this. get()
|
|
9
|
+
* returns the last good value. hasFault()/valid() inspect the flag;
|
|
10
|
+
* reset() clears it.
|
|
11
|
+
*
|
|
12
|
+
* Overflow detection uses portable pure-C++14 PRE-CHECKS (bounds tests
|
|
13
|
+
* before the assignment). The reconstruction trick would compute first
|
|
14
|
+
* then check, but the initial computation is signed-overflow UB. Pre-checks
|
|
15
|
+
* avoid the UB, work at every width including int64_t (no wider promotion
|
|
16
|
+
* type needed), and need no __builtin_*_overflow (a GCC/clang extension
|
|
17
|
+
* that would require an AUTOSAR deviation).
|
|
18
|
+
*
|
|
19
|
+
* NO <type_traits> / <limits> includes: AVR's toolchain ships neither
|
|
20
|
+
* header. The type constraint (static_assert) and the per-width bounds come
|
|
21
|
+
* from the shared hand-rolled __tc_safe_traits block (safe-traits.ts), which
|
|
22
|
+
* uses only core-language features (template specialization, static_cast,
|
|
23
|
+
* constexpr) that every supported target — including AVR's avr-gcc — has.
|
|
24
|
+
*
|
|
25
|
+
* AUTOSAR C++14 compliance:
|
|
26
|
+
* - static_assert on T (rejects unsigned, bool, float, non-integer)
|
|
27
|
+
* - noexcept on all methods (A15-0-2)
|
|
28
|
+
* - [[nodiscard]] on get/hasFault/valid (A0-1-1)
|
|
29
|
+
* - constexpr constructor
|
|
30
|
+
* - static_cast throughout (no C-casts M5-0-7, no builtins M5-0-10,
|
|
31
|
+
* no malloc A18-5-10). Zero deviations required.
|
|
32
|
+
*/
|
|
33
|
+
export declare function safeIntPolyfill(): RuntimePolyfillIR;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { emitSafeTraits } from "./safe-traits.js";
|
|
2
|
+
/** SafeInt<T> polyfill: chainable bounds-checked signed-integer arithmetic.
|
|
3
|
+
*
|
|
4
|
+
* Mutating chain — every math op returns SafeInt<T>& so calls compose:
|
|
5
|
+
* x.add(5).mul(2).get()
|
|
6
|
+
*
|
|
7
|
+
* Sticky-fault overflow policy: on overflow or division error, set fault_
|
|
8
|
+
* and stop computing. Subsequent ops are no-ops returning *this. get()
|
|
9
|
+
* returns the last good value. hasFault()/valid() inspect the flag;
|
|
10
|
+
* reset() clears it.
|
|
11
|
+
*
|
|
12
|
+
* Overflow detection uses portable pure-C++14 PRE-CHECKS (bounds tests
|
|
13
|
+
* before the assignment). The reconstruction trick would compute first
|
|
14
|
+
* then check, but the initial computation is signed-overflow UB. Pre-checks
|
|
15
|
+
* avoid the UB, work at every width including int64_t (no wider promotion
|
|
16
|
+
* type needed), and need no __builtin_*_overflow (a GCC/clang extension
|
|
17
|
+
* that would require an AUTOSAR deviation).
|
|
18
|
+
*
|
|
19
|
+
* NO <type_traits> / <limits> includes: AVR's toolchain ships neither
|
|
20
|
+
* header. The type constraint (static_assert) and the per-width bounds come
|
|
21
|
+
* from the shared hand-rolled __tc_safe_traits block (safe-traits.ts), which
|
|
22
|
+
* uses only core-language features (template specialization, static_cast,
|
|
23
|
+
* constexpr) that every supported target — including AVR's avr-gcc — has.
|
|
24
|
+
*
|
|
25
|
+
* AUTOSAR C++14 compliance:
|
|
26
|
+
* - static_assert on T (rejects unsigned, bool, float, non-integer)
|
|
27
|
+
* - noexcept on all methods (A15-0-2)
|
|
28
|
+
* - [[nodiscard]] on get/hasFault/valid (A0-1-1)
|
|
29
|
+
* - constexpr constructor
|
|
30
|
+
* - static_cast throughout (no C-casts M5-0-7, no builtins M5-0-10,
|
|
31
|
+
* no malloc A18-5-10). Zero deviations required.
|
|
32
|
+
*/
|
|
33
|
+
export function safeIntPolyfill() {
|
|
34
|
+
return {
|
|
35
|
+
kind: "polyfill",
|
|
36
|
+
id: "safety_safe_int",
|
|
37
|
+
domain: "embedded",
|
|
38
|
+
requiredIncludes: [],
|
|
39
|
+
forwardDeclarations: [],
|
|
40
|
+
helperStructs: [],
|
|
41
|
+
helperFunctions: [`
|
|
42
|
+
${emitSafeTraits()}
|
|
43
|
+
|
|
44
|
+
#ifndef __TC_SAFE_INT_DEFINED
|
|
45
|
+
#define __TC_SAFE_INT_DEFINED
|
|
46
|
+
|
|
47
|
+
// No #include here: the fixed-width integer types come transitively from
|
|
48
|
+
// <Arduino.h> on embedded targets (which pulls <stdint.h>), mirroring the
|
|
49
|
+
// other safety polyfills (mode-table, vote). The shared __tc_safe_traits
|
|
50
|
+
// block (emitted above) is also header-free.
|
|
51
|
+
|
|
52
|
+
template <typename T>
|
|
53
|
+
struct SafeInt {
|
|
54
|
+
static_assert(__tc_safe_traits::is_integral<T>::value
|
|
55
|
+
&& __tc_safe_traits::is_signed<T>::value
|
|
56
|
+
&& !__tc_safe_traits::is_same<T, bool>::value,
|
|
57
|
+
"SafeInt<T> requires a signed integer T (int8_t/int16_t/int32_t/int64_t). "
|
|
58
|
+
"Unsigned, bool, floating-point, and non-integer types are rejected.");
|
|
59
|
+
|
|
60
|
+
private:
|
|
61
|
+
T value_;
|
|
62
|
+
bool fault_;
|
|
63
|
+
|
|
64
|
+
static constexpr T kMax = __tc_safe_traits::bounds<T>::kMax;
|
|
65
|
+
static constexpr T kMin = __tc_safe_traits::bounds<T>::kMin;
|
|
66
|
+
|
|
67
|
+
public:
|
|
68
|
+
// Default constructor: zero-initialized, no fault. Required because promoted
|
|
69
|
+
// top-level variables default-construct via '= {}' before their real
|
|
70
|
+
// initializer is assigned inside the entrypoint. Mirrors SafeVariable's
|
|
71
|
+
// default ctor.
|
|
72
|
+
constexpr SafeInt() noexcept : value_(0), fault_(false) {}
|
|
73
|
+
constexpr SafeInt(T initial) noexcept : value_(initial), fault_(false) {}
|
|
74
|
+
|
|
75
|
+
SafeInt<T>& add(T delta) noexcept {
|
|
76
|
+
if (fault_) { return *this; }
|
|
77
|
+
// Pre-check: overflow iff (delta>0 && value>Max-delta) || (delta<0 && value<Min-delta).
|
|
78
|
+
// (Max-delta) and (Min-delta) are safe because delta has the opposite sign
|
|
79
|
+
// of the bound being approached, so the subtraction cannot itself overflow.
|
|
80
|
+
if ((delta > 0 && value_ > kMax - delta) || (delta < 0 && value_ < kMin - delta)) {
|
|
81
|
+
fault_ = true;
|
|
82
|
+
return *this;
|
|
83
|
+
}
|
|
84
|
+
value_ = static_cast<T>(value_ + delta);
|
|
85
|
+
return *this;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
SafeInt<T>& sub(T delta) noexcept {
|
|
89
|
+
if (fault_) { return *this; }
|
|
90
|
+
// Direct pre-check (NOT add(-delta), since -delta is UB when delta==Min):
|
|
91
|
+
// value - delta > Max iff value > Max + delta (when delta < 0)
|
|
92
|
+
// value - delta < Min iff value < Min + delta (when delta > 0)
|
|
93
|
+
// (Max+delta)/(Min+delta) are safe — delta has the opposite sign of the bound.
|
|
94
|
+
if ((delta < 0 && value_ > kMax + delta) || (delta > 0 && value_ < kMin + delta)) {
|
|
95
|
+
fault_ = true;
|
|
96
|
+
return *this;
|
|
97
|
+
}
|
|
98
|
+
value_ = static_cast<T>(value_ - delta);
|
|
99
|
+
return *this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
SafeInt<T>& mul(T factor) noexcept {
|
|
103
|
+
if (fault_) { return *this; }
|
|
104
|
+
if (factor == 0 || value_ == 0) { value_ = 0; return *this; }
|
|
105
|
+
// Pre-check via absolute bounds. |value| > Max / |factor| => overflow.
|
|
106
|
+
// (Also catches the value==Min && factor==-1 case since |Min| > Max.)
|
|
107
|
+
// absWithoutUB: -(kMin) is UB, so compute |kMin| as -(kMin+1) + (-1) = kMax... no,
|
|
108
|
+
// |kMin| is kMax+1 which is NOT representable. The check absValue > kMax/absFactor
|
|
109
|
+
// still fires for value==kMin because absValue (computed without UB) exceeds any
|
|
110
|
+
// representable positive value when factor==-1. We compute |x| without UB below.
|
|
111
|
+
const T absValue = (value_ < 0) ? static_cast<T>(-(value_ + 1)) + (-1) : value_;
|
|
112
|
+
const T absFactor = (factor < 0) ? static_cast<T>(-(factor + 1)) + (-1) : factor;
|
|
113
|
+
if (absValue > kMax / absFactor) {
|
|
114
|
+
fault_ = true;
|
|
115
|
+
return *this;
|
|
116
|
+
}
|
|
117
|
+
value_ = static_cast<T>(value_ * factor);
|
|
118
|
+
return *this;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Named divide (not div) to avoid the cuttlefish reserved-name escaper
|
|
122
|
+
// renaming .div( to .div_( because div() is a C stdlib (<cstdlib>) name.
|
|
123
|
+
SafeInt<T>& divide(T d) noexcept {
|
|
124
|
+
if (fault_) { return *this; }
|
|
125
|
+
if (d == 0) { fault_ = true; return *this; }
|
|
126
|
+
// INT_MIN / -1 overflows.
|
|
127
|
+
if (value_ == kMin && d == static_cast<T>(-1)) { fault_ = true; return *this; }
|
|
128
|
+
value_ = static_cast<T>(value_ / d);
|
|
129
|
+
return *this;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
SafeInt<T>& mod(T d) noexcept {
|
|
133
|
+
if (fault_) { return *this; }
|
|
134
|
+
if (d == 0) { fault_ = true; return *this; }
|
|
135
|
+
if (value_ == kMin && d == static_cast<T>(-1)) { value_ = 0; return *this; }
|
|
136
|
+
value_ = static_cast<T>(value_ % d);
|
|
137
|
+
return *this;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
SafeInt<T>& negate() noexcept {
|
|
141
|
+
if (fault_) { return *this; }
|
|
142
|
+
if (value_ == kMin) { fault_ = true; return *this; }
|
|
143
|
+
value_ = static_cast<T>(-value_);
|
|
144
|
+
return *this;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Named absValue (not abs) to avoid colliding with Arduino's abs() macro
|
|
148
|
+
// (Arduino.h #defines abs(x)), which would mangle the method declaration on
|
|
149
|
+
// AVR. Same caution for any future method whose name matches an Arduino core
|
|
150
|
+
// macro (min/max/round/...).
|
|
151
|
+
SafeInt<T>& absValue() noexcept {
|
|
152
|
+
if (fault_) { return *this; }
|
|
153
|
+
if (value_ == kMin) { fault_ = true; return *this; }
|
|
154
|
+
if (value_ < 0) { value_ = static_cast<T>(-value_); }
|
|
155
|
+
return *this;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
[[nodiscard]] constexpr T get() const noexcept { return value_; }
|
|
159
|
+
[[nodiscard]] bool hasFault() const noexcept { return fault_; }
|
|
160
|
+
[[nodiscard]] bool valid() const noexcept { return !fault_; }
|
|
161
|
+
|
|
162
|
+
void reset(T newValue) noexcept { value_ = newValue; fault_ = false; }
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
#endif // __TC_SAFE_INT_DEFINED
|
|
166
|
+
`.trim()],
|
|
167
|
+
shimMacros: [],
|
|
168
|
+
dependencies: [],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Returns the C++ source for the __tc_safe_traits block. Idempotent via the
|
|
2
|
+
* __TC_SAFE_TRAITS_DEFINED guard, so emitting it from both the SafeInt and
|
|
3
|
+
* SafeVariable polyfills is harmless (the second include is a no-op). */
|
|
4
|
+
export declare function emitSafeTraits(): string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Hand-rolled C++ type traits for the safety polyfills.
|
|
2
|
+
//
|
|
3
|
+
// AVR's avr-gcc ships neither <type_traits> nor <limits>. The SafeInt and
|
|
4
|
+
// SafeVariable polyfills need is_integral / is_signed / is_same for their
|
|
5
|
+
// static_asserts, but must not include <type_traits>. This helper returns a
|
|
6
|
+
// self-contained C++14 block (no includes) that defines a __tc_safe_traits
|
|
7
|
+
// namespace with exactly the traits the polyfills use.
|
|
8
|
+
//
|
|
9
|
+
// SPECIALIZATION DISCIPLINE (avoids redefinition errors):
|
|
10
|
+
// On AVR, the fixed-width typedefs alias the built-ins: int8_t == signed char,
|
|
11
|
+
// int16_t == short, int32_t == long (int is 16-bit on AVR). Specializing for
|
|
12
|
+
// BOTH a typedef and its underlying built-in is a redefinition error. So:
|
|
13
|
+
// - is_integral / is_signed specialize the BUILT-IN types only (signed char,
|
|
14
|
+
// short, int, long, long long, unsigned variants, char, bool). A lookup
|
|
15
|
+
// via a typedef (is_integral<int32_t>) resolves to the built-in
|
|
16
|
+
// specialization automatically because the typedef names the same type.
|
|
17
|
+
// - bounds specializes the FIXED-WIDTH typedefs only (int8_t/int16_t/
|
|
18
|
+
// int32_t/int64_t). Each typedef maps to exactly one specialization, and
|
|
19
|
+
// the bounds values match the typedef's width (not the host 'int' width).
|
|
20
|
+
/** Returns the C++ source for the __tc_safe_traits block. Idempotent via the
|
|
21
|
+
* __TC_SAFE_TRAITS_DEFINED guard, so emitting it from both the SafeInt and
|
|
22
|
+
* SafeVariable polyfills is harmless (the second include is a no-op). */
|
|
23
|
+
export function emitSafeTraits() {
|
|
24
|
+
return `
|
|
25
|
+
#ifndef __TC_SAFE_TRAITS_DEFINED
|
|
26
|
+
#define __TC_SAFE_TRAITS_DEFINED
|
|
27
|
+
|
|
28
|
+
// No #include here: the integer types come transitively from <Arduino.h> on
|
|
29
|
+
// embedded targets (which pulls <stdint.h>) and from the test harness's own
|
|
30
|
+
// includes on native. This mirrors the other safety polyfills (mode-table,
|
|
31
|
+
// vote) which use uint32_t with no explicit include.
|
|
32
|
+
|
|
33
|
+
namespace __tc_safe_traits {
|
|
34
|
+
|
|
35
|
+
// is_integral<T>: specialized on canonical built-in integer types only.
|
|
36
|
+
// Lookups via fixed-width typedefs (int8_t/int32_t/etc.) resolve to these
|
|
37
|
+
// because each typedef names the same type as its underlying built-in.
|
|
38
|
+
template <typename T> struct is_integral { static constexpr bool value = false; };
|
|
39
|
+
template <> struct is_integral<bool> { static constexpr bool value = true; };
|
|
40
|
+
template <> struct is_integral<char> { static constexpr bool value = true; };
|
|
41
|
+
template <> struct is_integral<signed char> { static constexpr bool value = true; };
|
|
42
|
+
template <> struct is_integral<unsigned char> { static constexpr bool value = true; };
|
|
43
|
+
template <> struct is_integral<short> { static constexpr bool value = true; };
|
|
44
|
+
template <> struct is_integral<unsigned short> { static constexpr bool value = true; };
|
|
45
|
+
template <> struct is_integral<int> { static constexpr bool value = true; };
|
|
46
|
+
template <> struct is_integral<unsigned int> { static constexpr bool value = true; };
|
|
47
|
+
template <> struct is_integral<long> { static constexpr bool value = true; };
|
|
48
|
+
template <> struct is_integral<unsigned long> { static constexpr bool value = true; };
|
|
49
|
+
template <> struct is_integral<long long> { static constexpr bool value = true; };
|
|
50
|
+
template <> struct is_integral<unsigned long long> { static constexpr bool value = true; };
|
|
51
|
+
|
|
52
|
+
// is_signed<T>: specialized on canonical signed built-in integer types only.
|
|
53
|
+
template <typename T> struct is_signed { static constexpr bool value = false; };
|
|
54
|
+
template <> struct is_signed<signed char> { static constexpr bool value = true; };
|
|
55
|
+
template <> struct is_signed<short> { static constexpr bool value = true; };
|
|
56
|
+
template <> struct is_signed<int> { static constexpr bool value = true; };
|
|
57
|
+
template <> struct is_signed<long> { static constexpr bool value = true; };
|
|
58
|
+
template <> struct is_signed<long long> { static constexpr bool value = true; };
|
|
59
|
+
|
|
60
|
+
// is_same<T,U>: standard two-type trait.
|
|
61
|
+
template <typename T, typename U> struct is_same { static constexpr bool value = false; };
|
|
62
|
+
template <typename T> struct is_same<T, T> { static constexpr bool value = true; };
|
|
63
|
+
|
|
64
|
+
// bounds<T>: per-fixed-width min/max via literals + static_cast (no <limits>).
|
|
65
|
+
// Specialized on the FIXED-WIDTH typedefs only. Each typedef has a unique
|
|
66
|
+
// specialization whose bounds match that typedef's width (independent of the
|
|
67
|
+
// host 'int' width — critical because AVR's int is 16-bit while ESP32/native
|
|
68
|
+
// is 32-bit). int64_t is guarded because some freestanding toolchains lack it.
|
|
69
|
+
template <typename T> struct bounds;
|
|
70
|
+
template <> struct bounds<int8_t> {
|
|
71
|
+
static constexpr int8_t kMin = static_cast<int8_t>(static_cast<uint8_t>(0x80u));
|
|
72
|
+
static constexpr int8_t kMax = static_cast<int8_t>(0x7Fu);
|
|
73
|
+
};
|
|
74
|
+
template <> struct bounds<int16_t> {
|
|
75
|
+
static constexpr int16_t kMin = static_cast<int16_t>(static_cast<uint16_t>(0x8000u));
|
|
76
|
+
static constexpr int16_t kMax = static_cast<int16_t>(0x7FFFu);
|
|
77
|
+
};
|
|
78
|
+
template <> struct bounds<int32_t> {
|
|
79
|
+
static constexpr int32_t kMin = static_cast<int32_t>(static_cast<uint32_t>(0x80000000u));
|
|
80
|
+
static constexpr int32_t kMax = static_cast<int32_t>(0x7FFFFFFFu);
|
|
81
|
+
};
|
|
82
|
+
#if defined(__INT64_TYPE__)
|
|
83
|
+
template <> struct bounds<__INT64_TYPE__> {
|
|
84
|
+
// Use unsigned-long-long literals (standard 'ull' suffix) and cast — the
|
|
85
|
+
// 'ill' signed suffix is a GNU extension needing -fext-numeric-literals.
|
|
86
|
+
static constexpr __INT64_TYPE__ kMin = static_cast<__INT64_TYPE__>(static_cast<unsigned long long>(0x8000000000000000ull));
|
|
87
|
+
static constexpr __INT64_TYPE__ kMax = static_cast<__INT64_TYPE__>(static_cast<unsigned long long>(0x7FFFFFFFFFFFFFFFull));
|
|
88
|
+
};
|
|
89
|
+
#endif
|
|
90
|
+
|
|
91
|
+
} // namespace __tc_safe_traits
|
|
92
|
+
|
|
93
|
+
#endif // __TC_SAFE_TRAITS_DEFINED
|
|
94
|
+
`.trim();
|
|
95
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { RuntimePolyfillIR } from "../../api/index.js";
|
|
2
|
+
/** SafeVariable polyfill: 3-replica TMR with per-replica inversion for
|
|
3
|
+
* integral types, and plain 3-copy TMR for float/double.
|
|
4
|
+
*
|
|
5
|
+
* Combines TMR (triple modular redundancy) with inverted-redundancy for
|
|
6
|
+
* integral types: three replicas of the value, each stored alongside its
|
|
7
|
+
* bitwise inverse. On read, each replica is individually validated (XOR
|
|
8
|
+
* check), then the valid replicas are majority-voted. This gives both
|
|
9
|
+
* detection AND correction:
|
|
10
|
+
*
|
|
11
|
+
* - A single-bit SEU in any replica's value or inverse breaks that
|
|
12
|
+
* replica's XOR invariant → the corrupted replica is excluded from
|
|
13
|
+
* the vote, and the good replicas correct it.
|
|
14
|
+
* - If two replicas agree (majority), the value is returned with
|
|
15
|
+
* confidence. The corrupted replica is silently repaired on the next
|
|
16
|
+
* set().
|
|
17
|
+
* - For float/double, replicas are stored as plain copies (no inversion
|
|
18
|
+
* possible), and majority vote still corrects single-replica corruption.
|
|
19
|
+
*
|
|
20
|
+
* Fault signaling: hasFault() returns true iff the last get() found ZERO
|
|
21
|
+
* valid replicas (triple corruption). Callers should check hasFault() after
|
|
22
|
+
* get() on safety-critical paths. set() clears the fault flag.
|
|
23
|
+
*
|
|
24
|
+
* Memory cost: 3× T (same as plain TMR). The inversion is per-replica and
|
|
25
|
+
* does not add copies beyond the three TMR replicas.
|
|
26
|
+
*
|
|
27
|
+
* Arithmetic T only — a static_assert rejects non-arithmetic types (notably
|
|
28
|
+
* std::string) and bool (for which bitwise inversion is ill-defined) at
|
|
29
|
+
* compile time. float and double have explicit specializations.
|
|
30
|
+
*
|
|
31
|
+
* AUTOSAR C++14 compliance:
|
|
32
|
+
* - noexcept on all methods (A15-0-2)
|
|
33
|
+
* - [[nodiscard]] on get/valid/hasFault (A0-1-1)
|
|
34
|
+
* - constexpr constructors/methods
|
|
35
|
+
* - no malloc/new (A18-5-8/A18-5-10)
|
|
36
|
+
* - no C-style casts (M5-0-7)
|
|
37
|
+
* - fixed-width types via template parameter (A3-9-1)
|
|
38
|
+
*/
|
|
39
|
+
export declare function safeVariablePolyfill(): RuntimePolyfillIR;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { emitSafeTraits } from "./safe-traits.js";
|
|
2
|
+
/** SafeVariable polyfill: 3-replica TMR with per-replica inversion for
|
|
3
|
+
* integral types, and plain 3-copy TMR for float/double.
|
|
4
|
+
*
|
|
5
|
+
* Combines TMR (triple modular redundancy) with inverted-redundancy for
|
|
6
|
+
* integral types: three replicas of the value, each stored alongside its
|
|
7
|
+
* bitwise inverse. On read, each replica is individually validated (XOR
|
|
8
|
+
* check), then the valid replicas are majority-voted. This gives both
|
|
9
|
+
* detection AND correction:
|
|
10
|
+
*
|
|
11
|
+
* - A single-bit SEU in any replica's value or inverse breaks that
|
|
12
|
+
* replica's XOR invariant → the corrupted replica is excluded from
|
|
13
|
+
* the vote, and the good replicas correct it.
|
|
14
|
+
* - If two replicas agree (majority), the value is returned with
|
|
15
|
+
* confidence. The corrupted replica is silently repaired on the next
|
|
16
|
+
* set().
|
|
17
|
+
* - For float/double, replicas are stored as plain copies (no inversion
|
|
18
|
+
* possible), and majority vote still corrects single-replica corruption.
|
|
19
|
+
*
|
|
20
|
+
* Fault signaling: hasFault() returns true iff the last get() found ZERO
|
|
21
|
+
* valid replicas (triple corruption). Callers should check hasFault() after
|
|
22
|
+
* get() on safety-critical paths. set() clears the fault flag.
|
|
23
|
+
*
|
|
24
|
+
* Memory cost: 3× T (same as plain TMR). The inversion is per-replica and
|
|
25
|
+
* does not add copies beyond the three TMR replicas.
|
|
26
|
+
*
|
|
27
|
+
* Arithmetic T only — a static_assert rejects non-arithmetic types (notably
|
|
28
|
+
* std::string) and bool (for which bitwise inversion is ill-defined) at
|
|
29
|
+
* compile time. float and double have explicit specializations.
|
|
30
|
+
*
|
|
31
|
+
* AUTOSAR C++14 compliance:
|
|
32
|
+
* - noexcept on all methods (A15-0-2)
|
|
33
|
+
* - [[nodiscard]] on get/valid/hasFault (A0-1-1)
|
|
34
|
+
* - constexpr constructors/methods
|
|
35
|
+
* - no malloc/new (A18-5-8/A18-5-10)
|
|
36
|
+
* - no C-style casts (M5-0-7)
|
|
37
|
+
* - fixed-width types via template parameter (A3-9-1)
|
|
38
|
+
*/
|
|
39
|
+
export function safeVariablePolyfill() {
|
|
40
|
+
return {
|
|
41
|
+
kind: "polyfill",
|
|
42
|
+
id: "safety_safe_variable",
|
|
43
|
+
domain: "embedded",
|
|
44
|
+
requiredIncludes: [],
|
|
45
|
+
forwardDeclarations: [],
|
|
46
|
+
helperStructs: [],
|
|
47
|
+
helperFunctions: [`
|
|
48
|
+
${emitSafeTraits()}
|
|
49
|
+
|
|
50
|
+
#ifndef __TC_SAFE_VARIABLE_DEFINED
|
|
51
|
+
#define __TC_SAFE_VARIABLE_DEFINED
|
|
52
|
+
|
|
53
|
+
// ── SafeVariable: integer types (bitwise XOR validation per replica) ──
|
|
54
|
+
// Each replica stores value + ~value. On read, each replica is validated
|
|
55
|
+
// independently. Valid replicas are majority-voted; the corrupted one
|
|
56
|
+
// (if any) is excluded. This provides both detection AND correction.
|
|
57
|
+
template <typename T>
|
|
58
|
+
struct SafeVariable {
|
|
59
|
+
// Arithmetic T only. bool is rejected explicitly because ~bool and ^bool
|
|
60
|
+
// undergo integer promotion and do not behave as a 1-bit inversion.
|
|
61
|
+
// float/double are handled by the explicit specializations below.
|
|
62
|
+
static_assert(__tc_safe_traits::is_integral<T>::value && !__tc_safe_traits::is_same<T, bool>::value,
|
|
63
|
+
"SafeVariable<T> requires an integral T (use float/double specializations for those; "
|
|
64
|
+
"std::string and bool are not supported)");
|
|
65
|
+
|
|
66
|
+
private:
|
|
67
|
+
T replicaA_val;
|
|
68
|
+
T replicaA_inv;
|
|
69
|
+
T replicaB_val;
|
|
70
|
+
T replicaB_inv;
|
|
71
|
+
T replicaC_val;
|
|
72
|
+
T replicaC_inv;
|
|
73
|
+
// mutable: get() is logically const (it does not change the stored value)
|
|
74
|
+
// but caches the "last read saw triple corruption" fault flag so callers
|
|
75
|
+
// can inspect it via hasFault() after get().
|
|
76
|
+
mutable bool fault_;
|
|
77
|
+
template <typename U> friend struct SafeVariable;
|
|
78
|
+
|
|
79
|
+
static bool replicaValid(T val, T inv) noexcept {
|
|
80
|
+
return (val ^ inv) == static_cast<T>(~static_cast<T>(0));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public:
|
|
84
|
+
SafeVariable() noexcept
|
|
85
|
+
: replicaA_val(0), replicaA_inv(static_cast<T>(~static_cast<T>(0))),
|
|
86
|
+
replicaB_val(0), replicaB_inv(static_cast<T>(~static_cast<T>(0))),
|
|
87
|
+
replicaC_val(0), replicaC_inv(static_cast<T>(~static_cast<T>(0))),
|
|
88
|
+
fault_(false) {}
|
|
89
|
+
|
|
90
|
+
SafeVariable(T initial) noexcept
|
|
91
|
+
: replicaA_val(initial), replicaA_inv(static_cast<T>(~initial)),
|
|
92
|
+
replicaB_val(initial), replicaB_inv(static_cast<T>(~initial)),
|
|
93
|
+
replicaC_val(initial), replicaC_inv(static_cast<T>(~initial)),
|
|
94
|
+
fault_(false) {}
|
|
95
|
+
|
|
96
|
+
template <typename U>
|
|
97
|
+
SafeVariable(const SafeVariable<U>& other) noexcept
|
|
98
|
+
: replicaA_val(static_cast<T>(other.replicaA_val)), replicaA_inv(static_cast<T>(other.replicaA_inv)),
|
|
99
|
+
replicaB_val(static_cast<T>(other.replicaB_val)), replicaB_inv(static_cast<T>(other.replicaB_inv)),
|
|
100
|
+
replicaC_val(static_cast<T>(other.replicaC_val)), replicaC_inv(static_cast<T>(other.replicaC_inv)),
|
|
101
|
+
fault_(other.fault_) {}
|
|
102
|
+
|
|
103
|
+
// Majority vote: returns the agreed-upon value from valid replicas.
|
|
104
|
+
// If a replica is corrupted (XOR check fails), it is excluded from
|
|
105
|
+
// the vote. If two or more valid replicas agree, return that value.
|
|
106
|
+
// If only one replica is valid, return it as a best-effort fallback.
|
|
107
|
+
// If NO replica is valid (triple corruption), returns replicaC_val as a
|
|
108
|
+
// last resort and sets the fault flag — callers must check hasFault().
|
|
109
|
+
[[nodiscard]] T get() const noexcept {
|
|
110
|
+
const bool aValid = replicaValid(replicaA_val, replicaA_inv);
|
|
111
|
+
const bool bValid = replicaValid(replicaB_val, replicaB_inv);
|
|
112
|
+
const bool cValid = replicaValid(replicaC_val, replicaC_inv);
|
|
113
|
+
|
|
114
|
+
// Two valid replicas agree → majority.
|
|
115
|
+
if (aValid && bValid && (replicaA_val == replicaB_val)) { fault_ = false; return replicaA_val; }
|
|
116
|
+
if (aValid && cValid && (replicaA_val == replicaC_val)) { fault_ = false; return replicaA_val; }
|
|
117
|
+
if (bValid && cValid && (replicaB_val == replicaC_val)) { fault_ = false; return replicaB_val; }
|
|
118
|
+
|
|
119
|
+
// Only one valid replica → return it (no majority but best available).
|
|
120
|
+
if (aValid) { fault_ = false; return replicaA_val; }
|
|
121
|
+
if (bValid) { fault_ = false; return replicaB_val; }
|
|
122
|
+
if (cValid) { fault_ = false; return replicaC_val; }
|
|
123
|
+
|
|
124
|
+
// Zero valid replicas → triple corruption. Flag the fault and return
|
|
125
|
+
// replicaC_val as a degenerate fallback; callers MUST check hasFault().
|
|
126
|
+
fault_ = true;
|
|
127
|
+
return replicaC_val;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Returns true if all three replicas pass their XOR checks AND agree.
|
|
131
|
+
// Does not mutate the fault flag (it reflects the last get(), not valid()).
|
|
132
|
+
[[nodiscard]] bool valid() const noexcept {
|
|
133
|
+
return replicaValid(replicaA_val, replicaA_inv)
|
|
134
|
+
&& replicaValid(replicaB_val, replicaB_inv)
|
|
135
|
+
&& replicaValid(replicaC_val, replicaC_inv)
|
|
136
|
+
&& (replicaA_val == replicaB_val)
|
|
137
|
+
&& (replicaB_val == replicaC_val);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Returns true iff the most recent get() found zero valid replicas
|
|
141
|
+
// (triple corruption). Cleared by set() and by a subsequent get() that
|
|
142
|
+
// finds at least one valid replica.
|
|
143
|
+
[[nodiscard]] bool hasFault() const noexcept { return fault_; }
|
|
144
|
+
|
|
145
|
+
void set(T newValue) noexcept {
|
|
146
|
+
const T inv = static_cast<T>(~newValue);
|
|
147
|
+
replicaA_val = newValue; replicaA_inv = inv;
|
|
148
|
+
replicaB_val = newValue; replicaB_inv = inv;
|
|
149
|
+
replicaC_val = newValue; replicaC_inv = inv;
|
|
150
|
+
fault_ = false;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// ── SafeVariable: float (3-replica majority vote, no inversion) ──
|
|
155
|
+
// Floats can't use bitwise XOR/inversion. Three identical copies are
|
|
156
|
+
// stored and majority-voted. A single corrupted copy is outvoted and
|
|
157
|
+
// silently repaired on the next set().
|
|
158
|
+
template <>
|
|
159
|
+
struct SafeVariable<float> {
|
|
160
|
+
private:
|
|
161
|
+
float replicaA;
|
|
162
|
+
float replicaB;
|
|
163
|
+
float replicaC;
|
|
164
|
+
mutable bool fault_;
|
|
165
|
+
template <typename U> friend struct SafeVariable;
|
|
166
|
+
public:
|
|
167
|
+
SafeVariable() noexcept : replicaA(0.0f), replicaB(0.0f), replicaC(0.0f), fault_(false) {}
|
|
168
|
+
SafeVariable(float initial) noexcept : replicaA(initial), replicaB(initial), replicaC(initial), fault_(false) {}
|
|
169
|
+
[[nodiscard]] float get() const noexcept {
|
|
170
|
+
if (replicaA == replicaB) { fault_ = false; return replicaA; }
|
|
171
|
+
if (replicaA == replicaC) { fault_ = false; return replicaA; }
|
|
172
|
+
if (replicaB == replicaC) { fault_ = false; return replicaB; }
|
|
173
|
+
// No two replicas agree — triple corruption.
|
|
174
|
+
fault_ = true;
|
|
175
|
+
return replicaC;
|
|
176
|
+
}
|
|
177
|
+
[[nodiscard]] bool valid() const noexcept {
|
|
178
|
+
return (replicaA == replicaB) && (replicaB == replicaC);
|
|
179
|
+
}
|
|
180
|
+
[[nodiscard]] bool hasFault() const noexcept { return fault_; }
|
|
181
|
+
void set(float newValue) noexcept {
|
|
182
|
+
replicaA = newValue;
|
|
183
|
+
replicaB = newValue;
|
|
184
|
+
replicaC = newValue;
|
|
185
|
+
fault_ = false;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// ── SafeVariable: double (3-replica majority vote, no inversion) ──
|
|
190
|
+
template <>
|
|
191
|
+
struct SafeVariable<double> {
|
|
192
|
+
private:
|
|
193
|
+
double replicaA;
|
|
194
|
+
double replicaB;
|
|
195
|
+
double replicaC;
|
|
196
|
+
mutable bool fault_;
|
|
197
|
+
template <typename U> friend struct SafeVariable;
|
|
198
|
+
public:
|
|
199
|
+
SafeVariable() noexcept : replicaA(0.0), replicaB(0.0), replicaC(0.0), fault_(false) {}
|
|
200
|
+
SafeVariable(double initial) noexcept : replicaA(initial), replicaB(initial), replicaC(initial), fault_(false) {}
|
|
201
|
+
[[nodiscard]] double get() const noexcept {
|
|
202
|
+
if (replicaA == replicaB) { fault_ = false; return replicaA; }
|
|
203
|
+
if (replicaA == replicaC) { fault_ = false; return replicaA; }
|
|
204
|
+
if (replicaB == replicaC) { fault_ = false; return replicaB; }
|
|
205
|
+
// No two replicas agree — triple corruption.
|
|
206
|
+
fault_ = true;
|
|
207
|
+
return replicaC;
|
|
208
|
+
}
|
|
209
|
+
[[nodiscard]] bool valid() const noexcept {
|
|
210
|
+
return (replicaA == replicaB) && (replicaB == replicaC);
|
|
211
|
+
}
|
|
212
|
+
[[nodiscard]] bool hasFault() const noexcept { return fault_; }
|
|
213
|
+
void set(double newValue) noexcept {
|
|
214
|
+
replicaA = newValue;
|
|
215
|
+
replicaB = newValue;
|
|
216
|
+
replicaC = newValue;
|
|
217
|
+
fault_ = false;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
#endif // __TC_SAFE_VARIABLE_DEFINED
|
|
222
|
+
`.trim()],
|
|
223
|
+
shimMacros: [],
|
|
224
|
+
dependencies: [],
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RuntimePolyfillIR } from "../../api/index.js";
|
|
2
|
+
/** The 2-of-3 voter. Tree-shaken out unless __tc_safety_read_safe appears
|
|
3
|
+
* in the program (registered in POLYFILL_HELPER_MAP).
|
|
4
|
+
*
|
|
5
|
+
* MCU-agnosticism: the voter reads via the strategy-injected __tc_gpio_read
|
|
6
|
+
* shim (defined per-target by each framework strategy's shimLines()). The
|
|
7
|
+
* safety package references zero target-specific symbols. */
|
|
8
|
+
export declare function voterPolyfill(): RuntimePolyfillIR;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/** The 2-of-3 voter. Tree-shaken out unless __tc_safety_read_safe appears
|
|
2
|
+
* in the program (registered in POLYFILL_HELPER_MAP).
|
|
3
|
+
*
|
|
4
|
+
* MCU-agnosticism: the voter reads via the strategy-injected __tc_gpio_read
|
|
5
|
+
* shim (defined per-target by each framework strategy's shimLines()). The
|
|
6
|
+
* safety package references zero target-specific symbols. */
|
|
7
|
+
export function voterPolyfill() {
|
|
8
|
+
return {
|
|
9
|
+
kind: "polyfill",
|
|
10
|
+
id: "safety_read_safe",
|
|
11
|
+
domain: "embedded",
|
|
12
|
+
requiredIncludes: [],
|
|
13
|
+
forwardDeclarations: [],
|
|
14
|
+
helperStructs: [],
|
|
15
|
+
helperFunctions: [`
|
|
16
|
+
// Forward declarations — the strategy's shimLines() defines these AFTER the
|
|
17
|
+
// polyfill block (polyfills emit before shims in emitPreamble). Without the
|
|
18
|
+
// forward decls, the voter's __tc_gpio_read / __tc_delay_us calls below would
|
|
19
|
+
// be unresolved.
|
|
20
|
+
int __tc_gpio_read(uint32_t pin);
|
|
21
|
+
void __tc_delay_us(uint32_t us);
|
|
22
|
+
|
|
23
|
+
namespace __tc_safety {
|
|
24
|
+
|
|
25
|
+
// Settle delay between temporally-separated reads. ~2 us is enough to defeat
|
|
26
|
+
// single-event transient (SET) glitches on a typical GPIO line while staying
|
|
27
|
+
// well under a debounce timescale. Tunable in one place.
|
|
28
|
+
constexpr uint32_t TC_SAFETY_SETTLE_US = 2U;
|
|
29
|
+
|
|
30
|
+
// Perform 3 temporally-separated reads via the strategy-injected shim, take
|
|
31
|
+
// the majority. On disagreement, VoteDisagreement. On pin-mode mismatch,
|
|
32
|
+
// PinModeMismatch. On untracked pin, PinModeUnknown. value is left 0 when
|
|
33
|
+
// ok==false — callers must check 'ok' before consuming 'value'.
|
|
34
|
+
inline SafeReadResult read_safe(uint32_t pin) {
|
|
35
|
+
SafeReadResult result;
|
|
36
|
+
result.value = 0x00000000U;
|
|
37
|
+
result.category = SafetyFaultCategory::Ok;
|
|
38
|
+
result.code = SafetyFaultCode::Ok;
|
|
39
|
+
result.status = SafetyStatus::Ok;
|
|
40
|
+
|
|
41
|
+
// 1. Confirm pin mode (Input, InputPullup, or InputPulldown are valid for
|
|
42
|
+
// reading). Output is rejected (PinModeMismatch). InputPulldown is
|
|
43
|
+
// accepted because it is a legitimate input mode on ESP32 (and AVR via
|
|
44
|
+
// external pulldown); rejecting it caused false faults.
|
|
45
|
+
const TrackedMode mode = get_pin_mode(pin);
|
|
46
|
+
if (mode == TrackedMode::Unknown) {
|
|
47
|
+
result.category = SafetyFaultCategory::Configuration;
|
|
48
|
+
result.code = SafetyFaultCode::PinModeUnknown;
|
|
49
|
+
result.status = SafetyStatus::Fault;
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
if ((mode != TrackedMode::Input) && (mode != TrackedMode::InputPullup) && (mode != TrackedMode::InputPulldown)) {
|
|
53
|
+
result.category = SafetyFaultCategory::Configuration;
|
|
54
|
+
result.code = SafetyFaultCode::PinModeMismatch;
|
|
55
|
+
result.status = SafetyStatus::Fault;
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 2. 2-of-3 vote via the strategy-injected __tc_gpio_read shim.
|
|
60
|
+
// __tc_delay_us provides temporal separation between reads so a single
|
|
61
|
+
// input glitch is unlikely to corrupt two samples; the strategy injects a
|
|
62
|
+
// real microsecond delay (Arduino/ESP32: delayMicroseconds; AVR:
|
|
63
|
+
// _native_delay_us; native: stub).
|
|
64
|
+
const uint32_t r0 = __tc_gpio_read(pin);
|
|
65
|
+
__tc_delay_us(TC_SAFETY_SETTLE_US);
|
|
66
|
+
const uint32_t r1 = __tc_gpio_read(pin);
|
|
67
|
+
__tc_delay_us(TC_SAFETY_SETTLE_US);
|
|
68
|
+
const uint32_t r2 = __tc_gpio_read(pin);
|
|
69
|
+
|
|
70
|
+
if ((r0 == r1) && (r1 == r2)) {
|
|
71
|
+
result.status = SafetyStatus::Ok;
|
|
72
|
+
result.value = r0 ? 0xFFFFFFFFU : 0x00000000U;
|
|
73
|
+
} else {
|
|
74
|
+
result.category = SafetyFaultCategory::Signal;
|
|
75
|
+
result.code = SafetyFaultCode::VoteDisagreement;
|
|
76
|
+
result.status = SafetyStatus::Fault;
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Namespace-scoped forwarding entry point — kept inside the namespace so
|
|
82
|
+
// the emitter's forward-declaration pass does not generate a global prototype
|
|
83
|
+
// that references SafeReadResult before the struct is defined.
|
|
84
|
+
inline SafeReadResult __tc_safety_read_safe(uint32_t pin) {
|
|
85
|
+
return read_safe(pin);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
} // namespace __tc_safety`.trim()],
|
|
89
|
+
shimMacros: [],
|
|
90
|
+
dependencies: ["safety_mode_table"],
|
|
91
|
+
};
|
|
92
|
+
}
|