@typecad/cuttlefish 1.0.0-alpha.13 → 1.0.0-alpha.15
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 +141 -0
- package/assets/editor-extensions/typecad-debug/out/extension.js +347 -0
- package/assets/editor-extensions/typecad-debug/out/extension.js.map +1 -0
- package/assets/editor-extensions/typecad-debug/package.json +76 -0
- package/assets/editor-extensions/typecad-ui/LICENSE +27 -0
- package/assets/editor-extensions/typecad-ui/README.md +52 -0
- package/assets/editor-extensions/typecad-ui/file-icon-dark.png +0 -0
- package/assets/editor-extensions/typecad-ui/file-icon-light.png +0 -0
- package/assets/editor-extensions/typecad-ui/icon.png +0 -0
- package/assets/editor-extensions/typecad-ui/language-configuration.json +43 -0
- package/assets/editor-extensions/typecad-ui/package.json +54 -0
- package/assets/editor-extensions/typecad-ui/snippets/typecad-ui.json +80 -0
- package/assets/editor-extensions/typecad-ui/syntaxes/markdown-ui.json +45 -0
- package/assets/editor-extensions/typecad-ui/syntaxes/typecad-ui.tmLanguage.json +1269 -0
- package/dist/api/config.d.ts +30 -27
- 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 +65 -507
- 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 +227 -244
- package/dist/config-loader.d.ts +14 -19
- package/dist/config-loader.js +242 -78
- package/dist/config-schema.d.ts +19 -227
- package/dist/config-schema.js +9 -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 +36 -127
- 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 +32 -0
- package/dist/create/editor-integration.js +257 -0
- package/dist/create/eslint-rules-template.d.ts +2 -2
- package/dist/create/framework-catalog.d.ts +48 -8
- package/dist/create/framework-catalog.js +58 -44
- package/dist/create/index.d.ts +8 -13
- package/dist/create/index.js +5 -9
- 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/{init-scaffold.d.ts → scaffold.d.ts} +8 -11
- package/dist/create/{init-scaffold.js → scaffold.js} +28 -87
- package/dist/create/templates.d.ts +51 -0
- package/dist/create/{init-templates.js → templates.js} +135 -116
- package/dist/create/wizard.d.ts +19 -0
- package/dist/create/wizard.js +314 -0
- 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 +147 -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 +64 -45
- 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/catalog.d.ts +35 -0
- package/dist/library/catalog.js +67 -0
- package/dist/library/cli.d.ts +2 -0
- package/dist/library/cli.js +168 -0
- package/dist/library/init.d.ts +25 -0
- package/dist/library/init.js +397 -0
- package/dist/library/install.d.ts +9 -0
- package/dist/library/install.js +80 -0
- package/dist/library/registry-search.d.ts +34 -0
- package/dist/library/registry-search.js +50 -0
- package/dist/library/validate.d.ts +17 -0
- package/dist/library/validate.js +175 -0
- package/dist/library-packages.d.ts +95 -0
- package/dist/library-packages.js +275 -0
- 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 +29 -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/preview/client.js +17 -3
- 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 +5 -6
- 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 +90 -29
- package/dist/types.d.ts +45 -26
- package/dist/ui-hook.d.ts +1 -1
- package/dist/utils/cli.d.ts +2 -2
- package/dist/utils/cli.js +160 -157
- 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 -149
- 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 -637
- package/dist/create/board-spec.js +0 -214
- package/dist/create/init-templates.d.ts +0 -28
- package/dist/create/init-wizard.d.ts +0 -8
- package/dist/create/init-wizard.js +0 -182
- 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,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
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RuntimePolyfillIR } from "../../api/index.js";
|
|
2
|
+
/** Write-then-readback verification. Tree-shaken out unless
|
|
3
|
+
* __tc_safety_write_verify appears in the program (registered in
|
|
4
|
+
* POLYFILL_HELPER_MAP).
|
|
5
|
+
*
|
|
6
|
+
* MCU-agnosticism: writes via the strategy-injected __tc_gpio_write shim,
|
|
7
|
+
* reads back via __tc_gpio_read — references zero target-specific symbols. */
|
|
8
|
+
export declare function writeVerifyPolyfill(): RuntimePolyfillIR;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** Write-then-readback verification. Tree-shaken out unless
|
|
2
|
+
* __tc_safety_write_verify appears in the program (registered in
|
|
3
|
+
* POLYFILL_HELPER_MAP).
|
|
4
|
+
*
|
|
5
|
+
* MCU-agnosticism: writes via the strategy-injected __tc_gpio_write shim,
|
|
6
|
+
* reads back via __tc_gpio_read — references zero target-specific symbols. */
|
|
7
|
+
export function writeVerifyPolyfill() {
|
|
8
|
+
return {
|
|
9
|
+
kind: "polyfill",
|
|
10
|
+
id: "safety_write_verify",
|
|
11
|
+
domain: "embedded",
|
|
12
|
+
requiredIncludes: [],
|
|
13
|
+
forwardDeclarations: [],
|
|
14
|
+
helperStructs: [],
|
|
15
|
+
helperFunctions: [`
|
|
16
|
+
// Forward declaration — the strategy's shimLines() defines this AFTER the
|
|
17
|
+
// polyfill block (polyfills emit before shims in emitPreamble).
|
|
18
|
+
void __tc_gpio_write(uint32_t pin, uint32_t value);
|
|
19
|
+
int __tc_gpio_read(uint32_t pin);
|
|
20
|
+
|
|
21
|
+
namespace __tc_safety {
|
|
22
|
+
|
|
23
|
+
inline SafeWriteResult write_verify(uint32_t pin, uint32_t value) {
|
|
24
|
+
SafeWriteResult result;
|
|
25
|
+
result.code = SafetyFaultCode::Ok;
|
|
26
|
+
result.status = SafetyStatus::Ok;
|
|
27
|
+
|
|
28
|
+
const TrackedMode mode = get_pin_mode(pin);
|
|
29
|
+
if (mode == TrackedMode::Unknown) {
|
|
30
|
+
result.code = SafetyFaultCode::PinModeUnknown;
|
|
31
|
+
result.status = SafetyStatus::Fault;
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
if (mode != TrackedMode::Output) {
|
|
35
|
+
result.code = SafetyFaultCode::PinModeMismatch;
|
|
36
|
+
result.status = SafetyStatus::Fault;
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
__tc_gpio_write(pin, (value != 0U) ? 0x1U : 0x0U);
|
|
41
|
+
|
|
42
|
+
// Read back the pin to verify the write took effect. On AVR/ESP32
|
|
43
|
+
// Arduino core, digitalRead on an OUTPUT pin returns the last value
|
|
44
|
+
// written to the PORT register, so this check catches software bugs
|
|
45
|
+
// (wrong pin, wrong mode) but not electrical faults (shorted pin,
|
|
46
|
+
// disconnected wire). True electrical verification requires external
|
|
47
|
+
// circuitry routing the output back to a separate input pin.
|
|
48
|
+
const int rd = __tc_gpio_read(pin);
|
|
49
|
+
const uint32_t readback = rd ? 0xFFFFFFFFU : 0x00000000U;
|
|
50
|
+
const uint32_t expected = (value != 0U) ? 0xFFFFFFFFU : 0x00000000U;
|
|
51
|
+
|
|
52
|
+
if (readback == expected) {
|
|
53
|
+
result.status = SafetyStatus::Ok;
|
|
54
|
+
result.code = SafetyFaultCode::Ok;
|
|
55
|
+
} else {
|
|
56
|
+
result.status = SafetyStatus::Fault;
|
|
57
|
+
result.code = SafetyFaultCode::WriteMismatch;
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
} // namespace __tc_safety
|
|
63
|
+
|
|
64
|
+
inline SafeWriteResult __tc_safety_write_verify(uint32_t pin, uint32_t value) {
|
|
65
|
+
return __tc_safety::write_verify(pin, value);
|
|
66
|
+
}
|
|
67
|
+
`.trim()],
|
|
68
|
+
shimMacros: [],
|
|
69
|
+
dependencies: ["safety_mode_table"],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** SafeInt<T> — chainable bounds-checked signed-integer arithmetic.
|
|
2
|
+
*
|
|
3
|
+
* The cuttlefish transpiler resolves SafeInt<number> to the C++ template
|
|
4
|
+
* form SafeInt<int32_t> (and SafeInt<int8_t>/<int16_t>/<int64_t> for the
|
|
5
|
+
* corresponding TS fixed-width aliases), and the safety polyfill provides
|
|
6
|
+
* the template definition.
|
|
7
|
+
*
|
|
8
|
+
* Signed integer T only. The emitted C++ template carries a static_assert
|
|
9
|
+
* that rejects unsigned integers, bool, floating-point, and non-integer
|
|
10
|
+
* types (notably string) at compile time.
|
|
11
|
+
*
|
|
12
|
+
* Mutating chain: every math operation returns the same SafeInt<T> (by
|
|
13
|
+
* reference) so calls chain: `x.add(5).mul(2).get()`. On overflow or
|
|
14
|
+
* division error, a sticky fault flag is set and subsequent operations in
|
|
15
|
+
* the chain become no-ops; get() returns the last good value. Check
|
|
16
|
+
* hasFault() (or valid()) after a chain on safety-critical paths.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* let x: SafeInt<int32_t> = SafeInt(10);
|
|
20
|
+
* x.add(5).mul(2); // mutating chain — x is now 30
|
|
21
|
+
* if (x.valid()) {
|
|
22
|
+
* const v: int32_t = x.get();
|
|
23
|
+
* }
|
|
24
|
+
*/
|
|
25
|
+
export interface SafeInt<T = number> {
|
|
26
|
+
add(delta: T): SafeInt<T>;
|
|
27
|
+
sub(delta: T): SafeInt<T>;
|
|
28
|
+
mul(factor: T): SafeInt<T>;
|
|
29
|
+
divide(d: T): SafeInt<T>;
|
|
30
|
+
mod(d: T): SafeInt<T>;
|
|
31
|
+
negate(): SafeInt<T>;
|
|
32
|
+
absValue(): SafeInt<T>;
|
|
33
|
+
get(): T;
|
|
34
|
+
/** True iff a prior operation in this chain overflowed or hit a division
|
|
35
|
+
* error. Sticky — once set, stays set until reset(). While true, all
|
|
36
|
+
* further math operations are no-ops and get() returns the last good
|
|
37
|
+
* value. */
|
|
38
|
+
hasFault(): boolean;
|
|
39
|
+
/** !hasFault(). Symmetric with SafeVariable.valid(). */
|
|
40
|
+
valid(): boolean;
|
|
41
|
+
/** Clear the fault flag and set a new value. The only way to re-arm a
|
|
42
|
+
* faulted SafeInt without reconstructing it. */
|
|
43
|
+
reset(newValue: T): void;
|
|
44
|
+
}
|
|
45
|
+
export declare function SafeInt<T>(initial: T): SafeInt<T>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** SafeInt<T> — chainable bounds-checked signed-integer arithmetic.
|
|
2
|
+
*
|
|
3
|
+
* The cuttlefish transpiler resolves SafeInt<number> to the C++ template
|
|
4
|
+
* form SafeInt<int32_t> (and SafeInt<int8_t>/<int16_t>/<int64_t> for the
|
|
5
|
+
* corresponding TS fixed-width aliases), and the safety polyfill provides
|
|
6
|
+
* the template definition.
|
|
7
|
+
*
|
|
8
|
+
* Signed integer T only. The emitted C++ template carries a static_assert
|
|
9
|
+
* that rejects unsigned integers, bool, floating-point, and non-integer
|
|
10
|
+
* types (notably string) at compile time.
|
|
11
|
+
*
|
|
12
|
+
* Mutating chain: every math operation returns the same SafeInt<T> (by
|
|
13
|
+
* reference) so calls chain: `x.add(5).mul(2).get()`. On overflow or
|
|
14
|
+
* division error, a sticky fault flag is set and subsequent operations in
|
|
15
|
+
* the chain become no-ops; get() returns the last good value. Check
|
|
16
|
+
* hasFault() (or valid()) after a chain on safety-critical paths.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* let x: SafeInt<int32_t> = SafeInt(10);
|
|
20
|
+
* x.add(5).mul(2); // mutating chain — x is now 30
|
|
21
|
+
* if (x.valid()) {
|
|
22
|
+
* const v: int32_t = x.get();
|
|
23
|
+
* }
|
|
24
|
+
*/
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** SafeVariable<T> — SEU-resistant storage with inverted-redundancy.
|
|
2
|
+
*
|
|
3
|
+
* The cuttlefish transpiler resolves SafeVariable<number> to the C++
|
|
4
|
+
* template form SafeVariable<int> (or int32_t under --autosar), and the
|
|
5
|
+
* safety polyfill provides the template definition.
|
|
6
|
+
*
|
|
7
|
+
* Arithmetic T only. The emitted C++ template carries a static_assert that
|
|
8
|
+
* rejects non-arithmetic types (notably string) and bool at compile time.
|
|
9
|
+
* float/double have explicit specializations (3-copy plain TMR — bitwise
|
|
10
|
+
* inversion is not defined for floating-point).
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* let x: SafeVariable<number> = SafeVariable(0);
|
|
14
|
+
* x.set(2000);
|
|
15
|
+
* const current = x.get(); // best-effort value
|
|
16
|
+
* if (x.hasFault()) { ... } // triple corruption — get() is untrusted
|
|
17
|
+
* if (x.valid()) { ... } // all three replicas agree and pass XOR checks
|
|
18
|
+
*/
|
|
19
|
+
export interface SafeVariable<T = number> {
|
|
20
|
+
set(value: T): void;
|
|
21
|
+
get(): T;
|
|
22
|
+
valid(): boolean;
|
|
23
|
+
/** True iff the most recent get() found zero valid replicas (triple
|
|
24
|
+
* corruption). Cleared by set() and by a subsequent get() that finds at
|
|
25
|
+
* least one valid replica. Check this after get() on safety-critical
|
|
26
|
+
* paths; a true return means the returned value is untrusted. */
|
|
27
|
+
hasFault(): boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare function SafeVariable(initial: number): SafeVariable<any>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** SafeVariable<T> — SEU-resistant storage with inverted-redundancy.
|
|
2
|
+
*
|
|
3
|
+
* The cuttlefish transpiler resolves SafeVariable<number> to the C++
|
|
4
|
+
* template form SafeVariable<int> (or int32_t under --autosar), and the
|
|
5
|
+
* safety polyfill provides the template definition.
|
|
6
|
+
*
|
|
7
|
+
* Arithmetic T only. The emitted C++ template carries a static_assert that
|
|
8
|
+
* rejects non-arithmetic types (notably string) and bool at compile time.
|
|
9
|
+
* float/double have explicit specializations (3-copy plain TMR — bitwise
|
|
10
|
+
* inversion is not defined for floating-point).
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* let x: SafeVariable<number> = SafeVariable(0);
|
|
14
|
+
* x.set(2000);
|
|
15
|
+
* const current = x.get(); // best-effort value
|
|
16
|
+
* if (x.hasFault()) { ... } // triple corruption — get() is untrusted
|
|
17
|
+
* if (x.valid()) { ... } // all three replicas agree and pass XOR checks
|
|
18
|
+
*/
|
|
19
|
+
export {};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* Safe to call when @typecad/safety is absent (hook stays null). */
|
|
1
|
+
/** Load the safety engine and register the hook. Called by transpile.ts at
|
|
2
|
+
* the start of each transpile run. */
|
|
4
3
|
export declare function loadSafetyEngine(): Promise<void>;
|
|
5
4
|
/** Reset the safety bridge: forget a load was attempted and clear the hook.
|
|
6
5
|
* Mirrors resetUIEngine() so each transpile run / test starts clean. */
|
|
7
6
|
export declare function resetSafetyEngine(): void;
|
|
8
|
-
/** Test-only: force the bridge into the "
|
|
9
|
-
* load as already attempted and leave the hook null, exactly as
|
|
10
|
-
* dynamic import
|
|
7
|
+
/** Test-only: force the bridge into the "safety not loaded" state — mark the
|
|
8
|
+
* load as already attempted and leave the hook null, exactly as the old
|
|
9
|
+
* dynamic import behaved when @typecad/safety was not installed. */
|
|
11
10
|
export declare function __simulateSafetyAbsentForTest(): void;
|
|
@@ -1,37 +1,24 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// Safety bridge —
|
|
2
|
+
// Safety bridge — registers the built-in safety engine.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
4
|
+
// The engine used to ship as the optional @typecad/safety package and was
|
|
5
|
+
// loaded via a dynamic package import; it now lives in this package under
|
|
6
|
+
// src/safety/. transpile.ts still calls loadSafetyEngine() at the start of
|
|
7
|
+
// transpileFile(), and the hook seam (safety-hook.ts) is unchanged so the
|
|
8
|
+
// degradation paths (resetSafetyEngine / __simulateSafetyAbsentForTest) keep
|
|
9
|
+
// working for tests.
|
|
8
10
|
// ---------------------------------------------------------------------------
|
|
9
11
|
import { setSafetyHook } from "../safety-hook.js";
|
|
10
12
|
let loaded = false;
|
|
11
|
-
/**
|
|
12
|
-
*
|
|
13
|
-
* Safe to call when @typecad/safety is absent (hook stays null). */
|
|
13
|
+
/** Load the safety engine and register the hook. Called by transpile.ts at
|
|
14
|
+
* the start of each transpile run. */
|
|
14
15
|
export async function loadSafetyEngine() {
|
|
15
16
|
if (loaded)
|
|
16
17
|
return;
|
|
17
18
|
loaded = true;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// the result as `any` and never resolves @typecad/safety's declaration
|
|
22
|
-
// files while type-checking this file. A literal specifier would make tsc
|
|
23
|
-
// load safety's dist/engine-index.d.ts, which imports back
|
|
24
|
-
// @typecad/cuttlefish/safety-hook-types — a self-referencing package path
|
|
25
|
-
// that resolves into this package's own dist/ output and causes TS5055
|
|
26
|
-
// ("would overwrite input file") on every rebuild where dist/ exists.
|
|
27
|
-
const safetyEnginePath = "@typecad/safety/engine";
|
|
28
|
-
const engine = await import(safetyEnginePath);
|
|
29
|
-
const hook = engine.registerSafetyEngine();
|
|
30
|
-
setSafetyHook(hook);
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
// @typecad/safety is not installed — cuttlefish works without safety.
|
|
34
|
-
}
|
|
19
|
+
const engine = await import("./engine.js");
|
|
20
|
+
const hook = engine.registerSafetyEngine();
|
|
21
|
+
setSafetyHook(hook);
|
|
35
22
|
}
|
|
36
23
|
/** Reset the safety bridge: forget a load was attempted and clear the hook.
|
|
37
24
|
* Mirrors resetUIEngine() so each transpile run / test starts clean. */
|
|
@@ -39,9 +26,9 @@ export function resetSafetyEngine() {
|
|
|
39
26
|
loaded = false;
|
|
40
27
|
setSafetyHook(null);
|
|
41
28
|
}
|
|
42
|
-
/** Test-only: force the bridge into the "
|
|
43
|
-
* load as already attempted and leave the hook null, exactly as
|
|
44
|
-
* dynamic import
|
|
29
|
+
/** Test-only: force the bridge into the "safety not loaded" state — mark the
|
|
30
|
+
* load as already attempted and leave the hook null, exactly as the old
|
|
31
|
+
* dynamic import behaved when @typecad/safety was not installed. */
|
|
45
32
|
export function __simulateSafetyAbsentForTest() {
|
|
46
33
|
loaded = true;
|
|
47
34
|
setSafetyHook(null);
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import type { SafetyMetadata } from "../safety-hook.js";
|
|
2
|
-
/**
|
|
3
|
-
* Uses a variable-held specifier so tsc doesn't try to resolve the
|
|
4
|
-
* @typecad/safety types at build time (avoids circular dependency). */
|
|
2
|
+
/** Render the safety sidecar artifact via the built-in ISO 26262 writer. */
|
|
5
3
|
export declare function renderSafetySidecar(metadata: SafetyMetadata, toolVersion: string): Promise<string>;
|
|
@@ -1,22 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* @typecad/safety types at build time (avoids circular dependency). */
|
|
1
|
+
import { renderSafetySidecar as renderSidecar } from "./iso26262/sidecar-writer.js";
|
|
2
|
+
/** Render the safety sidecar artifact via the built-in ISO 26262 writer. */
|
|
4
3
|
export async function renderSafetySidecar(metadata, toolVersion) {
|
|
5
|
-
|
|
6
|
-
try {
|
|
7
|
-
const mod = await import(spec);
|
|
8
|
-
return mod.renderSafetySidecar(metadata, toolVersion);
|
|
9
|
-
}
|
|
10
|
-
catch {
|
|
11
|
-
// Fallback: inline the renderer so the sidecar is produced even if
|
|
12
|
-
// the subpath export isn't configured.
|
|
13
|
-
return JSON.stringify({
|
|
14
|
-
schemaVersion: "1.0.0",
|
|
15
|
-
standard: "ISO 26262 Part 6",
|
|
16
|
-
tool: "cuttlefish (@typecad/safety)",
|
|
17
|
-
toolVersion,
|
|
18
|
-
generatedAt: new Date().toISOString(),
|
|
19
|
-
safetyFunctions: metadata.functions,
|
|
20
|
-
}, null, 2);
|
|
21
|
-
}
|
|
4
|
+
return renderSidecar(metadata, toolVersion);
|
|
22
5
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The canonical specifier for the safety authoring surface. */
|
|
2
|
+
export declare const SAFETY_SPECIFIER = "@typecad/cuttlefish/safety";
|
|
3
|
+
/** The legacy specifier from when safety was a standalone package. */
|
|
4
|
+
export declare const LEGACY_SAFETY_SPECIFIER = "@typecad/safety";
|
|
5
|
+
/** True iff an import specifier refers to the safety authoring surface. */
|
|
6
|
+
export declare function isSafetyImportSpecifier(moduleSpecifier: string): boolean;
|