@typecad/cuttlefish 1.0.0-alpha.7 → 1.0.0-alpha.9
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/dist/api/config.d.ts +32 -0
- package/dist/api/index.d.ts +1 -1
- package/dist/api/schema/types.d.ts +15 -0
- package/dist/api/shared/async-runtime-static.d.ts +12 -1
- package/dist/api/shared/async-runtime-static.js +69 -24
- package/dist/api/shared/async-symbol-detector.d.ts +11 -0
- package/dist/api/shared/async-symbol-detector.js +140 -0
- package/dist/api/shared/async-types.d.ts +24 -0
- package/dist/api/shared/coop-scheduler.d.ts +60 -0
- package/dist/api/shared/coop-scheduler.js +149 -0
- package/dist/api/shared/display-adapter.d.ts +2 -2
- package/dist/api/shared/display-adapter.js +9 -119
- package/dist/api/shared/display-adapters/sdl.js +29 -29
- package/dist/api/shared/display-profile.d.ts +38 -33
- package/dist/api/shared/display-profile.js +22 -170
- package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
- package/dist/api/shared/framework-manifest-registry.js +8 -6
- package/dist/api/shared/framework-manifest.d.ts +147 -28
- package/dist/api/shared/framework-manifest.js +35 -2
- package/dist/api/shared/graphics-strategy.d.ts +15 -6
- package/dist/api/shared/hal-op-ir.d.ts +340 -3
- package/dist/api/shared/hal-op-ir.js +34 -0
- package/dist/api/shared/index.d.ts +9 -1
- package/dist/api/shared/index.js +11 -1
- package/dist/api/shared/ir-core.d.ts +4 -0
- package/dist/api/shared/ir-declarations.d.ts +6 -0
- package/dist/api/shared/platform-strategy.d.ts +104 -2
- package/dist/api/shared/polyfill-helper-registry.js +18 -1
- package/dist/api/shared/promise-runtime.d.ts +1 -1
- package/dist/api/shared/promise-runtime.js +17 -13
- package/dist/api/shared/toolchain-types.d.ts +17 -0
- package/dist/api/shared/validate-framework-manifest.js +77 -0
- package/dist/api/shared/worker-runtime-polyfill.d.ts +22 -0
- package/dist/api/shared/worker-runtime-polyfill.js +34 -0
- package/dist/api/shared/worker-runtime.d.ts +69 -0
- package/dist/api/shared/worker-runtime.js +163 -0
- package/dist/cli.js +125 -121
- package/dist/config-loader.d.ts +7 -2
- package/dist/config-loader.js +20 -19
- package/dist/config-schema.d.ts +107 -65
- package/dist/config-schema.js +14 -0
- package/dist/contract/board-generator.d.ts +17 -0
- package/dist/contract/board-generator.js +57 -0
- package/dist/contract/contract-parser.d.ts +217 -0
- package/dist/contract/contract-parser.js +224 -0
- package/dist/contract/index.d.ts +22 -0
- package/dist/contract/index.js +68 -0
- package/dist/create/board-codegen.js +4 -4
- package/dist/create/board-generators.js +4 -5
- package/dist/create/board-spec.d.ts +74 -77
- package/dist/create/board-spec.js +0 -1
- package/dist/create/init-scaffold.d.ts +7 -3
- package/dist/create/init-scaffold.js +0 -62
- package/dist/create/init-templates.js +62 -16
- package/dist/create/init-wizard.js +32 -27
- package/dist/debug/preprocessor.js +178 -39
- package/dist/debug/types.d.ts +23 -0
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +34 -24
- package/dist/emit/compliance/arxml-writer.d.ts +11 -0
- package/dist/emit/compliance/arxml-writer.js +34 -0
- package/dist/emit/compliance/compliance-context.d.ts +57 -0
- package/dist/emit/compliance/compliance-context.js +113 -0
- package/dist/emit/compliance/deviation-ledger.d.ts +27 -0
- package/dist/emit/compliance/deviation-ledger.js +47 -0
- package/dist/emit/compliance/deviation-writer.d.ts +30 -0
- package/dist/emit/compliance/deviation-writer.js +37 -0
- package/dist/emit/compliance/index.d.ts +7 -0
- package/dist/emit/compliance/index.js +6 -0
- package/dist/emit/compliance/rule-engine.d.ts +13 -0
- package/dist/emit/compliance/rule-engine.js +101 -0
- package/dist/emit/compliance/rules.d.ts +16 -0
- package/dist/emit/compliance/rules.js +191 -0
- package/dist/emit/compliance/types.d.ts +59 -0
- package/dist/emit/compliance/types.js +8 -0
- package/dist/emit/emitters/class-emitter.js +6 -1
- package/dist/emit/emitters/emitter-context.d.ts +23 -1
- package/dist/emit/emitters/function-emitter-impl.js +96 -6
- package/dist/emit/emitters/line-appender.js +19 -0
- package/dist/emit/emitters/line-marker.d.ts +38 -0
- package/dist/emit/emitters/line-marker.js +39 -0
- package/dist/emit/emitters/output-finalizer.js +80 -1
- package/dist/emit/emitters/setup.d.ts +17 -0
- package/dist/emit/emitters/setup.js +217 -36
- package/dist/emit/emitters/top-level-prep.js +6 -0
- package/dist/emit/emitters/type-decl-emitter.js +28 -3
- package/dist/emit/emitters/ui-emitter.js +17 -8
- package/dist/emit/expression-renderer.d.ts +1 -1
- package/dist/emit/expression-renderer.js +81 -5
- package/dist/emit/route-hal-op.js +18 -5
- package/dist/emit/snprintf-helpers.js +7 -4
- package/dist/emit/statement-renderer.d.ts +10 -0
- package/dist/emit/statement-renderer.js +21 -5
- package/dist/emit/utils/async-state-machine.js +39 -12
- package/dist/framework-package.js +2 -0
- package/dist/framework-registry.d.ts +17 -0
- package/dist/install/framework-catalog.d.ts +53 -0
- package/dist/install/framework-catalog.js +107 -0
- package/dist/install/handle-install.d.ts +35 -0
- package/dist/install/handle-install.js +177 -0
- package/dist/install/index.d.ts +4 -0
- package/dist/install/index.js +3 -0
- package/dist/ir/adc-range-validation.js +14 -1
- package/dist/ir/build-ir-state.d.ts +1 -0
- package/dist/ir/build-ir-state.js +14 -0
- package/dist/ir/build-ir.js +4 -4
- package/dist/ir/call-graph.js +16 -0
- package/dist/ir/expression-to-ir.js +150 -0
- package/dist/ir/function-builder.js +22 -0
- package/dist/ir/hal/hal-emitter.js +10 -6
- package/dist/ir/hal/hal-parser.js +16 -9
- package/dist/ir/hal/hal-plugins.js +164 -3
- package/dist/ir/identifier-collector.js +21 -4
- package/dist/ir/interrupt-analysis.d.ts +5 -1
- package/dist/ir/interrupt-analysis.js +8 -16
- package/dist/ir/ownership-analysis.js +6 -0
- package/dist/ir/pin-capability-validation.js +37 -0
- package/dist/ir/pin-mode-validation.d.ts +2 -2
- package/dist/ir/pin-mode-validation.js +49 -18
- package/dist/ir/program-analysis.d.ts +42 -0
- package/dist/ir/program-analysis.js +243 -6
- package/dist/ir/render-expr.js +11 -0
- package/dist/ir/timing-validation.js +11 -2
- package/dist/ir/transformers/call-statement.js +105 -0
- package/dist/ir/transformers/expressions.js +4 -0
- package/dist/ir/transformers/hal-call-resolver.js +21 -0
- package/dist/ir/transformers/namespace-methods.js +17 -12
- package/dist/ir/transformers/ui-reactive.js +2 -2
- package/dist/ir/transformers/variables.js +51 -0
- package/dist/ir/type-resolution.js +18 -0
- package/dist/ir/ui-element-auto-wire.js +7 -5
- package/dist/ir/utils/map-statements.d.ts +4 -0
- package/dist/ir/utils/map-statements.js +79 -0
- package/dist/ir/validation-orchestrator.js +6 -1
- package/dist/ir/worker-analysis.d.ts +10 -0
- package/dist/ir/worker-analysis.js +261 -0
- package/dist/orchestrator/graph-builder.js +8 -7
- package/dist/platform/coop-scheduler-runtime.d.ts +19 -0
- package/dist/platform/coop-scheduler-runtime.js +41 -0
- package/dist/platform/generic-strategy.d.ts +15 -3
- package/dist/platform/generic-strategy.js +49 -4
- package/dist/safety/safety-bridge.d.ts +11 -0
- package/dist/safety/safety-bridge.js +48 -0
- package/dist/safety/sidecar-bridge.d.ts +5 -0
- package/dist/safety/sidecar-bridge.js +22 -0
- package/dist/safety-hook.d.ts +79 -0
- package/dist/safety-hook.js +35 -0
- package/dist/stores/display-profile-store.d.ts +0 -1
- package/dist/stores/display-profile-store.js +0 -1
- package/dist/testing.d.ts +4 -0
- package/dist/testing.js +5 -0
- package/dist/transpile.js +124 -35
- package/dist/types.d.ts +22 -2
- package/dist/ui-hook.d.ts +0 -2
- package/dist/utils/cli.d.ts +2 -2
- package/dist/utils/cli.js +68 -3
- package/package.json +20 -5
- package/dist/api/shared/display-adapters/eink-mono.d.ts +0 -2
- package/dist/api/shared/display-adapters/eink-mono.js +0 -53
- package/dist/api/shared/display-adapters/ssd1309.d.ts +0 -2
- package/dist/api/shared/display-adapters/ssd1309.js +0 -136
- package/dist/api/shared/display-adapters/st7796.d.ts +0 -2
- package/dist/api/shared/display-adapters/st7796.js +0 -132
- package/dist/libdef/idf-discovery.d.ts +0 -7
- package/dist/libdef/idf-discovery.js +0 -59
- package/dist/licenses.d.ts +0 -185
- package/dist/licenses.js +0 -963
|
@@ -108,8 +108,13 @@ export interface PlatformTypeStrategy {
|
|
|
108
108
|
* when the enum members map directly to platform preprocessor macros.
|
|
109
109
|
*/
|
|
110
110
|
passthroughEnumNames?(): ReadonlySet<string>;
|
|
111
|
-
/**
|
|
112
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Default numeric type for the platform (e.g. "int" on hosted, "int32_t" on embedded).
|
|
113
|
+
* Optional compliance context: when A3-9-1 is enforced, returns a fixed-width type.
|
|
114
|
+
*/
|
|
115
|
+
defaultNumericType(compliance?: {
|
|
116
|
+
isBanned(ruleId: string): boolean;
|
|
117
|
+
}): string;
|
|
113
118
|
/**
|
|
114
119
|
* Inform the strategy which enums have values exceeding the signed 16-bit range.
|
|
115
120
|
* The strategy can use this to choose an appropriate underlying type.
|
|
@@ -307,12 +312,32 @@ export interface PlatformBuildStrategy {
|
|
|
307
312
|
/** Standard library support for the given architecture. */
|
|
308
313
|
getStdLibSupport(architecture?: string): StdLibSupport;
|
|
309
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Coarse C++ type category carried per debug variable, so printf-based codegens
|
|
317
|
+
* (ESP-IDF) can pick the right format specifier. Inferred by the debug
|
|
318
|
+
* preprocessor from AST shape (no TypeChecker); `unknown` is the fallback.
|
|
319
|
+
* Mirrors `DebugCppType` in cuttlefish/src/debug/types.ts — duplicated here to
|
|
320
|
+
* avoid a cross-module import from the api surface.
|
|
321
|
+
*/
|
|
322
|
+
export type DebugCppType = 'bool' | 'int' | 'long' | 'float' | 'string' | 'unknown';
|
|
310
323
|
/**
|
|
311
324
|
* Debug code generation sub-interface.
|
|
312
325
|
* Frameworks implement these to provide platform-specific debug output
|
|
313
326
|
* (e.g., Serial.println on embedded, std::cout on hosted targets).
|
|
314
327
|
*/
|
|
315
328
|
export interface PlatformDebugStrategy {
|
|
329
|
+
/**
|
|
330
|
+
* Debug mode for the given build target. Controls whether `--debug` emits
|
|
331
|
+
* GDB-oriented output (`#line` directives + debug config artifacts, value
|
|
332
|
+
* `'gdb'`) or the legacy printf/Serial instrumentation (`'printf'`).
|
|
333
|
+
*
|
|
334
|
+
* Frameworks that support native source-level debugging for a chip return
|
|
335
|
+
* `'gdb'` for that target; everything else inherits the `'printf'` default
|
|
336
|
+
* and the existing instrumentation path is used unchanged.
|
|
337
|
+
*
|
|
338
|
+
* @param target the frameworkData.buildTarget value (e.g. 'esp32s3')
|
|
339
|
+
*/
|
|
340
|
+
debugMode?(target?: string): 'gdb' | 'printf';
|
|
316
341
|
/** Generate initialization code for the debug subsystem. */
|
|
317
342
|
generateDebugInitCode?(): string[];
|
|
318
343
|
/** Generate code for a breakpoint with optional condition and variable dump. */
|
|
@@ -323,8 +348,16 @@ export interface PlatformDebugStrategy {
|
|
|
323
348
|
variables: Array<{
|
|
324
349
|
name: string;
|
|
325
350
|
isFunction?: boolean;
|
|
351
|
+
cppType?: DebugCppType;
|
|
326
352
|
}>;
|
|
327
353
|
normalizedCondition?: string;
|
|
354
|
+
/**
|
|
355
|
+
* Stable per-file ID assigned by the preprocessor. When present, the
|
|
356
|
+
* codegen emits a `static bool __tc_bp_disabled_<id>` flag and wraps the
|
|
357
|
+
* halt in `if (!__tc_bp_disabled_<id>)`, so the user can skip/disable this
|
|
358
|
+
* one breakpoint for the rest of the run (the 's' key sets the flag).
|
|
359
|
+
*/
|
|
360
|
+
breakpointId?: number;
|
|
328
361
|
}): string[];
|
|
329
362
|
/** Generate code for a logpoint with interpolated message parts. */
|
|
330
363
|
generateDebugLogpointCode?(params: {
|
|
@@ -337,6 +370,7 @@ export interface PlatformDebugStrategy {
|
|
|
337
370
|
variables: Array<{
|
|
338
371
|
name: string;
|
|
339
372
|
isFunction?: boolean;
|
|
373
|
+
cppType?: DebugCppType;
|
|
340
374
|
}>;
|
|
341
375
|
}): string[];
|
|
342
376
|
}
|
|
@@ -371,6 +405,27 @@ export interface PlatformAsyncStrategy {
|
|
|
371
405
|
preIteration: string;
|
|
372
406
|
postIteration: string;
|
|
373
407
|
} | null;
|
|
408
|
+
/**
|
|
409
|
+
* C++ statements that spawn the worker for slot `handleId`, running
|
|
410
|
+
* `fn(arg)`. Emitted as the body of __tc_worker_submit. Return undefined
|
|
411
|
+
* to declare worker offload unsupported on this framework.
|
|
412
|
+
*
|
|
413
|
+
* The shared runtime has already recorded `waiter`/set `done=false` before
|
|
414
|
+
* this runs; the hook need only spawn. `fn` and `arg` are C++ expressions
|
|
415
|
+
* (a function pointer and a void*).
|
|
416
|
+
*/
|
|
417
|
+
workerSpawnLines?(handleId: number, fn: string, arg: string): string[] | undefined;
|
|
418
|
+
/**
|
|
419
|
+
* C++ expression the worker calls AFTER completing its work and writing its
|
|
420
|
+
* outputs, to signal completion to the waiter. Emitted inside the worker
|
|
421
|
+
* trampoline. Must issue a memory barrier. Return undefined if unsupported.
|
|
422
|
+
*/
|
|
423
|
+
workerSignalDoneExpr?(handleId: number): string | undefined;
|
|
424
|
+
/**
|
|
425
|
+
* C++ boolean expression polled by __tc_worker_done to test completion.
|
|
426
|
+
* Must observe the signal's memory barrier. Return undefined if unsupported.
|
|
427
|
+
*/
|
|
428
|
+
workerIsDoneExpr?(handleId: number): string | undefined;
|
|
374
429
|
}
|
|
375
430
|
/**
|
|
376
431
|
* Strategy for resolving semantic HAL operations to framework-specific C++.
|
|
@@ -403,6 +458,53 @@ export interface PlatformHALStrategy {
|
|
|
403
458
|
* Default true (preserves existing behavior for hardware strategies).
|
|
404
459
|
*/
|
|
405
460
|
modelsGpio?(): boolean;
|
|
461
|
+
/**
|
|
462
|
+
* C++ expression that reads the digital level of a pin (HIGH/LOW).
|
|
463
|
+
* ArduinoStrategy returns `digitalRead(${pin})`. GenericStrategy returns a
|
|
464
|
+
* documented no-op stub (no GPIO on host).
|
|
465
|
+
*/
|
|
466
|
+
readDigitalPin?(pin: string): string;
|
|
467
|
+
/**
|
|
468
|
+
* C++ expression that reads a raw analog value from an ADC pin.
|
|
469
|
+
* ArduinoStrategy returns `analogRead(${pin})`. GenericStrategy stubs.
|
|
470
|
+
*/
|
|
471
|
+
readAnalogPin?(pin: string): string;
|
|
472
|
+
/**
|
|
473
|
+
* C++ statement that writes a digital level to a pin.
|
|
474
|
+
* ArduinoStrategy returns `digitalWrite(${pin}, ${val})`. Cuttlefish does not
|
|
475
|
+
* call this in the current emit path (display reset moves to the framework),
|
|
476
|
+
* but it is part of the atomic surface for completeness/future use.
|
|
477
|
+
*/
|
|
478
|
+
writeDigitalPin?(pin: string, val: string): string;
|
|
479
|
+
/**
|
|
480
|
+
* C++ statement that configures a pin's mode.
|
|
481
|
+
* ArduinoStrategy returns `pinMode(${pin}, ${mode})`.
|
|
482
|
+
*/
|
|
483
|
+
setPinMode?(pin: string, mode: string): string;
|
|
484
|
+
/**
|
|
485
|
+
* C++ statement that blocks for a number of milliseconds.
|
|
486
|
+
* ArduinoStrategy returns `delay(${ms})`.
|
|
487
|
+
*/
|
|
488
|
+
delayMs?(ms: string): string;
|
|
489
|
+
/**
|
|
490
|
+
* C++ statement that blocks for a number of microseconds.
|
|
491
|
+
* ArduinoStrategy returns `delayMicroseconds(${us})`.
|
|
492
|
+
*/
|
|
493
|
+
delayMicroseconds?(us: string): string;
|
|
494
|
+
/**
|
|
495
|
+
* The full set of HAL call names this framework's emitted code may contain
|
|
496
|
+
* (digitalRead, analogRead, Serial, tone, millis, …). Used to recognize HAL
|
|
497
|
+
* calls in rendered C++ text where no structured HAL-IR node is available.
|
|
498
|
+
*/
|
|
499
|
+
halCallNames?(): ReadonlySet<string>;
|
|
500
|
+
/** Membership test against {@link halCallNames}. Default false on generic. */
|
|
501
|
+
isHalCall?(name: string): boolean;
|
|
502
|
+
/**
|
|
503
|
+
* The subset of {@link halCallNames} that return an analog-read integer
|
|
504
|
+
* value (e.g. analogRead). Used by adc-range-validation to detect ADC reads
|
|
505
|
+
* in lowered raw text.
|
|
506
|
+
*/
|
|
507
|
+
analogReadCallNames?(): ReadonlySet<string>;
|
|
406
508
|
}
|
|
407
509
|
/**
|
|
408
510
|
* Full platform strategy composed from focused sub-interfaces.
|
|
@@ -62,6 +62,22 @@ export const POLYFILL_HELPER_MAP = {
|
|
|
62
62
|
// JSON helpers
|
|
63
63
|
'__tc_jsonStringify(': ['__tc_jsonStringify'],
|
|
64
64
|
'__tc_jsonParse(': ['__tc_jsonParse'],
|
|
65
|
+
// Safety helpers — emitted by @typecad/safety's polyfills as global-scope
|
|
66
|
+
// __tc_safety_* free functions (the polyfill-helper-registry extractor
|
|
67
|
+
// expects __tc_* immediately followed by `(`, so namespaced names would be
|
|
68
|
+
// missed). The call-pattern keys are the substrings program analysis scans
|
|
69
|
+
// source for; the values are the __tc_safety_* names that
|
|
70
|
+
// filterPolyfillHelpers matches in helperFunctions text.
|
|
71
|
+
//
|
|
72
|
+
// Two key forms per helper: the user-facing safe.* source spelling (which
|
|
73
|
+
// program analysis sees in raw IR before lowering) and the lowered
|
|
74
|
+
// __tc_safety_* spelling (which appears after the safety call-statement
|
|
75
|
+
// transformer emits hal-op resolution text). Both map to the same helper.
|
|
76
|
+
'safe.read(': ['__tc_safety_read_safe'],
|
|
77
|
+
'safe.write(': ['__tc_safety_write_verify'],
|
|
78
|
+
'__tc_safety_record_pin_mode(': ['__tc_safety_record_pin_mode'],
|
|
79
|
+
'__tc_safety_read_safe(': ['__tc_safety_read_safe'],
|
|
80
|
+
'__tc_safety_write_verify(': ['__tc_safety_write_verify'],
|
|
65
81
|
};
|
|
66
82
|
function extractHelperFunctionNames(funcDef) {
|
|
67
83
|
const matches = funcDef.matchAll(/\b(__tc_[A-Za-z0-9_]+)\s*\(/g);
|
|
@@ -82,5 +98,6 @@ export function filterPolyfillHelpers(polyfills, usedHelpers) {
|
|
|
82
98
|
}),
|
|
83
99
|
})).filter(polyfill => polyfill.helperFunctions.length > 0 ||
|
|
84
100
|
polyfill.forwardDeclarations.length > 0 ||
|
|
85
|
-
polyfill.helperStructs.length > 0
|
|
101
|
+
polyfill.helperStructs.length > 0 ||
|
|
102
|
+
polyfill.shimMacros.length > 0);
|
|
86
103
|
}
|
|
@@ -7,4 +7,4 @@
|
|
|
7
7
|
* @param includeWaitForPinEdge Whether to include the `waitForPinEdge` stub
|
|
8
8
|
* (used by Arduino targets).
|
|
9
9
|
*/
|
|
10
|
-
export declare function generatePromiseRuntime(queueCapacity: number, includeWaitForPinEdge?: boolean): string;
|
|
10
|
+
export declare function generatePromiseRuntime(queueCapacity: number, includeWaitForPinEdge?: boolean, strategy?: import("./platform-strategy.js").PlatformStrategy): string;
|
|
@@ -20,7 +20,11 @@
|
|
|
20
20
|
* @param includeWaitForPinEdge Whether to include the `waitForPinEdge` stub
|
|
21
21
|
* (used by Arduino targets).
|
|
22
22
|
*/
|
|
23
|
-
export function generatePromiseRuntime(queueCapacity, includeWaitForPinEdge = false) {
|
|
23
|
+
export function generatePromiseRuntime(queueCapacity, includeWaitForPinEdge = false, strategy) {
|
|
24
|
+
// The current-time expression (millis() on Wiring-derived frameworks,
|
|
25
|
+
// std::chrono on generic). Falling back to millis() preserves the historical
|
|
26
|
+
// behavior when no strategy is supplied.
|
|
27
|
+
const now = strategy?.currentTimeMillis?.() ?? "millis()";
|
|
24
28
|
const waitForPinEdge = includeWaitForPinEdge ? `
|
|
25
29
|
// HAL-level wait for pin edge — polling-based implementation.
|
|
26
30
|
// Detects an actual transition (idle→target), not just the current level.
|
|
@@ -31,18 +35,18 @@ export function generatePromiseRuntime(queueCapacity, includeWaitForPinEdge = fa
|
|
|
31
35
|
return Promise<void>([pin, mode, timeout](std::function<void(const void*)> resolve, std::function<void(const std::string&)> reject) {
|
|
32
36
|
int targetState = (mode == RISING) ? HIGH : LOW;
|
|
33
37
|
int idleState = (mode == RISING) ? LOW : HIGH;
|
|
34
|
-
unsigned long start =
|
|
38
|
+
unsigned long start = ${now};
|
|
35
39
|
// Phase 2 poller: waits for the pin to reach the target state (the edge).
|
|
36
40
|
auto pollTarget = [pin, targetState, timeout, start, resolve]() {
|
|
37
|
-
if (digitalRead(pin) == targetState) {
|
|
41
|
+
if (${strategy?.readDigitalPin?.("pin") ?? "digitalRead(pin)"} == targetState) {
|
|
38
42
|
resolve(nullptr);
|
|
39
|
-
} else if (timeout >= 0 && (
|
|
43
|
+
} else if (timeout >= 0 && (${now} - start >= static_cast<unsigned long>(timeout))) {
|
|
40
44
|
resolve(nullptr);
|
|
41
45
|
} else {
|
|
42
46
|
enqueueMicrotask([pin, targetState, timeout, start, resolve]() {
|
|
43
|
-
if (digitalRead(pin) == targetState) {
|
|
47
|
+
if (${strategy?.readDigitalPin?.("pin") ?? "digitalRead(pin)"} == targetState) {
|
|
44
48
|
resolve(nullptr);
|
|
45
|
-
} else if (timeout >= 0 && (
|
|
49
|
+
} else if (timeout >= 0 && (${now} - start >= static_cast<unsigned long>(timeout))) {
|
|
46
50
|
resolve(nullptr);
|
|
47
51
|
} else {
|
|
48
52
|
enqueueMicrotask([pin, targetState, timeout, start, resolve]() {});
|
|
@@ -52,9 +56,9 @@ export function generatePromiseRuntime(queueCapacity, includeWaitForPinEdge = fa
|
|
|
52
56
|
};
|
|
53
57
|
// Phase 1: wait for idle state before watching for the edge.
|
|
54
58
|
enqueueMicrotask([pin, idleState, timeout, start, resolve, pollTarget]() {
|
|
55
|
-
if (digitalRead(pin) == idleState) {
|
|
59
|
+
if (${strategy?.readDigitalPin?.("pin") ?? "digitalRead(pin)"} == idleState) {
|
|
56
60
|
pollTarget();
|
|
57
|
-
} else if (timeout >= 0 && (
|
|
61
|
+
} else if (timeout >= 0 && (${now} - start >= static_cast<unsigned long>(timeout))) {
|
|
58
62
|
resolve(nullptr);
|
|
59
63
|
} else {
|
|
60
64
|
enqueueMicrotask([pin, idleState, timeout, start, resolve, pollTarget]() {});
|
|
@@ -264,12 +268,12 @@ namespace typecad_async {
|
|
|
264
268
|
// Async.sleep() — cooperative delay using millis polling
|
|
265
269
|
inline Promise<void> __cuttlefish_async_sleep(unsigned long ms) {
|
|
266
270
|
return Promise<void>([ms](std::function<void(const void*)> resolve, std::function<void(const std::string&)> reject) {
|
|
267
|
-
unsigned long start =
|
|
268
|
-
if (
|
|
271
|
+
unsigned long start = ${now};
|
|
272
|
+
if (${now} - start >= ms) {
|
|
269
273
|
resolve(nullptr);
|
|
270
274
|
} else {
|
|
271
275
|
enqueueMicrotask([ms, start, resolve]() {
|
|
272
|
-
if (
|
|
276
|
+
if (${now} - start >= ms) {
|
|
273
277
|
resolve(nullptr);
|
|
274
278
|
} else {
|
|
275
279
|
enqueueMicrotask([ms, start, resolve]() { /* will be re-checked next cycle */ });
|
|
@@ -289,9 +293,9 @@ namespace typecad_async {
|
|
|
289
293
|
// Async.sleepUntil() — poll condition every interval ms
|
|
290
294
|
inline Promise<void> __cuttlefish_async_sleep_until(unsigned long pollIntervalMs) {
|
|
291
295
|
return Promise<void>([pollIntervalMs](std::function<void(const void*)> resolve, std::function<void(const std::string&)> reject) {
|
|
292
|
-
unsigned long start =
|
|
296
|
+
unsigned long start = ${now};
|
|
293
297
|
enqueueMicrotask([pollIntervalMs, start, resolve]() {
|
|
294
|
-
if (
|
|
298
|
+
if (${now} - start >= pollIntervalMs) {
|
|
295
299
|
resolve(nullptr); // caller re-checks condition
|
|
296
300
|
} else {
|
|
297
301
|
enqueueMicrotask([pollIntervalMs, start, resolve]() { /* re-check next cycle */ });
|
|
@@ -31,11 +31,28 @@ export interface ToolchainOptions {
|
|
|
31
31
|
optimize?: string;
|
|
32
32
|
extraFlags?: string[];
|
|
33
33
|
defines?: Record<string, string>;
|
|
34
|
+
/** ESP32 PSRAM type ('opi' | 'quad') when the target board has PSRAM. */
|
|
35
|
+
psram?: 'opi' | 'quad';
|
|
34
36
|
/**
|
|
35
37
|
* Framework-specific config from `cuttlefish.config.ts`.
|
|
36
38
|
* Each framework casts this to its own typed interface.
|
|
37
39
|
*/
|
|
38
40
|
frameworkConfig?: Record<string, unknown>;
|
|
41
|
+
/**
|
|
42
|
+
* Zephyr-specific config from `cuttlefish.config.ts` (the `zephyr` section).
|
|
43
|
+
*/
|
|
44
|
+
zephyrConfig?: Record<string, unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* Display config from `cuttlefish.config.ts` (the `display` section).
|
|
47
|
+
* Frameworks use cs/dc/rst/spiFrequency to generate devicetree wiring.
|
|
48
|
+
*/
|
|
49
|
+
display?: Record<string, unknown>;
|
|
50
|
+
/**
|
|
51
|
+
* True when `--debug` is active. Frameworks read this to generate debug
|
|
52
|
+
* artifacts (e.g. VS Code launch configs, debug Kconfig symbols) and to
|
|
53
|
+
* select their debug mode (gdb vs printf) via PlatformDebugStrategy.
|
|
54
|
+
*/
|
|
55
|
+
debug?: boolean;
|
|
39
56
|
}
|
|
40
57
|
/**
|
|
41
58
|
* Generic compile result. All frameworks return this shape.
|
|
@@ -29,6 +29,7 @@ class Accumulator {
|
|
|
29
29
|
const CATEGORY_PREFIXES = {
|
|
30
30
|
gpio: ['gpio.'],
|
|
31
31
|
pwm: ['pwm.'],
|
|
32
|
+
rmt: ['rmt.'],
|
|
32
33
|
adc: ['adc.'],
|
|
33
34
|
dac: ['dac.'],
|
|
34
35
|
interrupts: ['interrupt.'],
|
|
@@ -46,6 +47,32 @@ const CATEGORY_PREFIXES = {
|
|
|
46
47
|
http: ['http.'],
|
|
47
48
|
display: ['display.'],
|
|
48
49
|
preferences: ['preferences.'],
|
|
50
|
+
ble: ['ble.'],
|
|
51
|
+
random: ['random.'],
|
|
52
|
+
fs: ['fs.'],
|
|
53
|
+
mdns: ['mdns.'],
|
|
54
|
+
mqtt: ['mqtt.'],
|
|
55
|
+
ota: ['ota.'],
|
|
56
|
+
temp: ['temp.'],
|
|
57
|
+
hwtimer: ['hwtimer.'],
|
|
58
|
+
capacitive: ['capacitive.'],
|
|
59
|
+
// Unimplemented-but-recognized categories: each framework may declare these
|
|
60
|
+
// as unsupported in its manifest so the coverage matrix records them as a
|
|
61
|
+
// roadmap. The validator probes the resolver and confirms it does NOT lower
|
|
62
|
+
// them (consistent with the 'unsupported' status).
|
|
63
|
+
i2s: ['i2s.'],
|
|
64
|
+
twai: ['twai.'],
|
|
65
|
+
usb: ['usb.'],
|
|
66
|
+
eth: ['eth.'],
|
|
67
|
+
espnow: ['espnow.'],
|
|
68
|
+
crypto: ['crypto.'],
|
|
69
|
+
pcnt: ['pcnt.'],
|
|
70
|
+
mcpwm: ['mcpwm.'],
|
|
71
|
+
// Worker offload + snprintf raw-escape: recognized categories. Frameworks
|
|
72
|
+
// that lower worker.* declare it supported; snprintf.emit is a raw escape
|
|
73
|
+
// hatch (declared unsupported by frameworks that don't special-case it).
|
|
74
|
+
worker: ['worker.'],
|
|
75
|
+
snprintf: ['snprintf.'],
|
|
49
76
|
};
|
|
50
77
|
function opKindsForCategory(category) {
|
|
51
78
|
const prefixes = CATEGORY_PREFIXES[category] ?? [];
|
|
@@ -78,6 +105,36 @@ const OP_PROBE_PAYLOADS = {
|
|
|
78
105
|
'display.draw_text': { x: 0, y: 0, text: 'x', font: '8x16', color: 0xffff },
|
|
79
106
|
'display.draw_rect': { x: 0, y: 0, w: 10, h: 10, color: 0xffff },
|
|
80
107
|
'display.flush': { rects: [{ x: 0, y: 0, w: 10, h: 10 }] },
|
|
108
|
+
// fs.* require a path (and content for write_text).
|
|
109
|
+
'fs.read_text': { path: '/sdcard/x.txt' },
|
|
110
|
+
'fs.write_text': { path: '/sdcard/x.txt', content: 'hi' },
|
|
111
|
+
'fs.exists': { path: '/sdcard/x.txt' },
|
|
112
|
+
'fs.remove': { path: '/sdcard/x.txt' },
|
|
113
|
+
// random.range requires min/max.
|
|
114
|
+
'random.range': { min: 0, max: 10 },
|
|
115
|
+
// mdns.add_service requires instance/proto/port.
|
|
116
|
+
'mdns.start': { hostname: 'h' },
|
|
117
|
+
'mdns.set_hostname': { name: 'h' },
|
|
118
|
+
'mdns.add_service': { instance: 'i', proto: '_tcp', port: 80 },
|
|
119
|
+
// mqtt.connect requires brokerUri/clientId; mqtt.publish requires topic/data.
|
|
120
|
+
'mqtt.connect': { brokerUri: 'mqtt://b', clientId: 'c' },
|
|
121
|
+
'mqtt.on_message': { handler: 'cb' },
|
|
122
|
+
'mqtt.subscribe': { topic: 't' },
|
|
123
|
+
'mqtt.publish': { topic: 't', data: 'd' },
|
|
124
|
+
// wifi.on_event requires an event + handler. Probe with 'connect'.
|
|
125
|
+
// Without a payload the probe sends event=undefined and a 'supported'
|
|
126
|
+
// declaration would false-fail as "resolver returned undefined".
|
|
127
|
+
'wifi.on_event': { event: 'connect', handler: 'cb' },
|
|
128
|
+
// ota.from_url requires url; ota.write requires chunk.
|
|
129
|
+
'ota.from_url': { url: 'https://x' },
|
|
130
|
+
'ota.write': { chunk: 'buf' },
|
|
131
|
+
// hwtimer.* require an instance (and hz/handler).
|
|
132
|
+
'hwtimer.set_frequency': { instance: 0, hz: 1000 },
|
|
133
|
+
'hwtimer.on_overflow': { instance: 0, handler: 'cb' },
|
|
134
|
+
'hwtimer.start': { instance: 0 },
|
|
135
|
+
'hwtimer.stop': { instance: 0 },
|
|
136
|
+
// capacitive.read requires pin.
|
|
137
|
+
'capacitive.read': { pin: 4 },
|
|
81
138
|
};
|
|
82
139
|
// Builds a HALOpIR probe. Uses OP_PROBE_PAYLOADS when available so resolvers
|
|
83
140
|
// that destructure required fields don't throw on the probe itself.
|
|
@@ -232,6 +289,25 @@ function validateHalCoverage(manifest, ctx, acc) {
|
|
|
232
289
|
}
|
|
233
290
|
}
|
|
234
291
|
// ---------------------------------------------------------------------------
|
|
292
|
+
// Category C2 — HAL completeness
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
/**
|
|
295
|
+
* Verify every known HAL category appears in manifest.hal. validateHalCoverage
|
|
296
|
+
* only audits categories the manifest declares; a category omitted entirely is
|
|
297
|
+
* invisible to it (and to the per-op check). This step closes that hole: each
|
|
298
|
+
* category in CATEGORY_PREFIXES must be accounted for, even as unsupported.
|
|
299
|
+
* `raw` is excluded — it is the schema-level escape hatch, not a real category,
|
|
300
|
+
* and is not in CATEGORY_PREFIXES.
|
|
301
|
+
*/
|
|
302
|
+
function validateHalCompleteness(manifest, _ctx, acc) {
|
|
303
|
+
const declared = new Set(Object.keys(manifest.hal));
|
|
304
|
+
for (const category of Object.keys(CATEGORY_PREFIXES)) {
|
|
305
|
+
if (!declared.has(category)) {
|
|
306
|
+
acc.error(`hal/${category}/category-undeclared`, `manifest.hal.${category}`, `manifest.hal is missing the "${category}" category. Add a block declaring it (supported: true/false with ops, or unsupported with unsupportedReason).`, category);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
235
311
|
// Category D — polyfills
|
|
236
312
|
// ---------------------------------------------------------------------------
|
|
237
313
|
function validatePolyfills(manifest, ctx, acc) {
|
|
@@ -404,6 +480,7 @@ export function validateFrameworkManifest(manifest, ctx) {
|
|
|
404
480
|
validateIdentity(manifest, ctx, acc);
|
|
405
481
|
validateEntrypoint(manifest, ctx, acc);
|
|
406
482
|
validateHalCoverage(manifest, ctx, acc);
|
|
483
|
+
validateHalCompleteness(manifest, ctx, acc);
|
|
407
484
|
validatePolyfills(manifest, ctx, acc);
|
|
408
485
|
validateToolchain(manifest, ctx, acc);
|
|
409
486
|
validateLibraryResolution(manifest, ctx, acc);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ProgramIR } from "../index.js";
|
|
2
|
+
import type { RuntimePolyfillIR } from "./index.js";
|
|
3
|
+
import type { PlatformStrategy } from "./platform-strategy.js";
|
|
4
|
+
import type { WorkerBacking } from "./worker-runtime.js";
|
|
5
|
+
/**
|
|
6
|
+
* Build a RuntimePolyfillIR for the worker-offload runtime, when the framework
|
|
7
|
+
* supplies worker backing via its PlatformAsyncStrategy hooks
|
|
8
|
+
* (workerSpawnLines / workerSignalDoneExpr / workerIsDoneExpr).
|
|
9
|
+
*
|
|
10
|
+
* The polyfill carries only the slot table + __tc_worker_submit/done contract
|
|
11
|
+
* with the per-framework primitives injected from the strategy hooks. STL-free
|
|
12
|
+
* so it links on minimal-libc targets (Zephyr). Returns null if the strategy
|
|
13
|
+
* supplies no backing (worker offload unsupported there) — in which case
|
|
14
|
+
* worker.* ops resolve to unsupported.
|
|
15
|
+
*
|
|
16
|
+
* Callers gate this on the program actually using worker.* ops
|
|
17
|
+
* (ProgramAnalysisResult.usesWorker); the producer does not re-scan.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildWorkerRuntimePolyfill(_program: ProgramIR, strategy: PlatformStrategy, backing: WorkerBacking, options?: {
|
|
20
|
+
poolSize?: number;
|
|
21
|
+
stackSize?: number;
|
|
22
|
+
}): RuntimePolyfillIR | null;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { generateWorkerRuntime } from "./worker-runtime.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build a RuntimePolyfillIR for the worker-offload runtime, when the framework
|
|
4
|
+
* supplies worker backing via its PlatformAsyncStrategy hooks
|
|
5
|
+
* (workerSpawnLines / workerSignalDoneExpr / workerIsDoneExpr).
|
|
6
|
+
*
|
|
7
|
+
* The polyfill carries only the slot table + __tc_worker_submit/done contract
|
|
8
|
+
* with the per-framework primitives injected from the strategy hooks. STL-free
|
|
9
|
+
* so it links on minimal-libc targets (Zephyr). Returns null if the strategy
|
|
10
|
+
* supplies no backing (worker offload unsupported there) — in which case
|
|
11
|
+
* worker.* ops resolve to unsupported.
|
|
12
|
+
*
|
|
13
|
+
* Callers gate this on the program actually using worker.* ops
|
|
14
|
+
* (ProgramAnalysisResult.usesWorker); the producer does not re-scan.
|
|
15
|
+
*/
|
|
16
|
+
export function buildWorkerRuntimePolyfill(_program, strategy, backing, options = {}) {
|
|
17
|
+
// The backing is authoritative: it is constructed by the framework from its
|
|
18
|
+
// own hooks. If a framework supplies no backing (worker offload unsupported),
|
|
19
|
+
// it should not call this producer.
|
|
20
|
+
const runtime = generateWorkerRuntime(backing, options);
|
|
21
|
+
if (!runtime)
|
|
22
|
+
return null;
|
|
23
|
+
return {
|
|
24
|
+
kind: "polyfill",
|
|
25
|
+
id: "worker_runtime",
|
|
26
|
+
domain: "embedded",
|
|
27
|
+
requiredIncludes: backing.requiredIncludes,
|
|
28
|
+
forwardDeclarations: backing.declarations ?? [],
|
|
29
|
+
helperStructs: [runtime],
|
|
30
|
+
helperFunctions: [],
|
|
31
|
+
shimMacros: [],
|
|
32
|
+
dependencies: [],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export interface WorkerRuntimeOptions {
|
|
2
|
+
/** Number of fixed worker slots in the pool. Default 4. */
|
|
3
|
+
poolSize?: number;
|
|
4
|
+
/** Per-slot worker stack size in bytes (ESP32). Default 8192. */
|
|
5
|
+
stackSize?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Per-framework backing primitives injected into the shared contract.
|
|
9
|
+
* Undefined fields mean "unsupported" and the runtime is not emitted.
|
|
10
|
+
*/
|
|
11
|
+
export interface WorkerBacking {
|
|
12
|
+
/**
|
|
13
|
+
* Statements that spawn the worker trampoline for slot `handleId`.
|
|
14
|
+
*
|
|
15
|
+
* `trampolineName` is the C++ symbol of the per-slot worker function the
|
|
16
|
+
* shared runtime generated (it reads fn/arg from the slot, runs them, then
|
|
17
|
+
* signals completion). `waiterExpr` is the C++ lvalue the backing should set
|
|
18
|
+
* to the current waiter handle before spawning (the shared runtime reads it
|
|
19
|
+
* via signalDoneExpr/isDoneExpr).
|
|
20
|
+
*
|
|
21
|
+
* The shared runtime has already stored the submitted fn/arg into the slot
|
|
22
|
+
* and cleared `done` before this runs; the backing need only record the
|
|
23
|
+
* waiter and spawn `trampolineName`.
|
|
24
|
+
*/
|
|
25
|
+
spawnLines: (handleId: number, trampolineName: string, waiterExpr: string) => string[];
|
|
26
|
+
/** Expression the worker calls after finishing + writing outputs (barrier). */
|
|
27
|
+
signalDoneExpr: (handleId: number) => string;
|
|
28
|
+
/** Boolean expression polled by __tc_worker_done (observes the barrier). */
|
|
29
|
+
isDoneExpr: (handleId: number) => string;
|
|
30
|
+
/** C++ headers required by the primitives (e.g. '<freertos/FreeRTOS.h>'). */
|
|
31
|
+
requiredIncludes: string[];
|
|
32
|
+
/** Any backing declarations (e.g. semaphore objects) emitted before the pool. */
|
|
33
|
+
declarations?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Per-slot backing declarations emitted inside the namespace, one call per
|
|
36
|
+
* slot, before the submit/done functions. Used by backings that need a named
|
|
37
|
+
* per-slot object bound to the trampoline at definition time — e.g. Zephyr's
|
|
38
|
+
* `K_WORK_DEFINE(_work_<i>, __tc_worker_fn_<i>)` + a per-slot semaphore.
|
|
39
|
+
* `trampolineName` is the per-slot worker symbol the shared runtime emitted.
|
|
40
|
+
*/
|
|
41
|
+
slotDeclarations?: (handleId: number, trampolineName: string) => string[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Generate the worker-offload runtime C++ for a framework that supplies a
|
|
45
|
+
* backing. Returns null if `backing` is null (unsupported on this framework).
|
|
46
|
+
*
|
|
47
|
+
* The emitted code defines, for each slot i in [0, poolSize):
|
|
48
|
+
* - a worker trampoline __tc_worker_fn_i(void* arg) that runs the recorded
|
|
49
|
+
* fn and then signals completion via signalDoneExpr,
|
|
50
|
+
* - __tc_worker_submit(i, fn, arg) — records the waiter, clears done, spawns,
|
|
51
|
+
* - __tc_worker_done(i) — poll predicate.
|
|
52
|
+
*
|
|
53
|
+
* AUTOSAR C++14: fixed-width types, static_cast, final structs, no STL, no heap.
|
|
54
|
+
*/
|
|
55
|
+
export declare function generateWorkerRuntime(backing: WorkerBacking | null, options?: WorkerRuntimeOptions): string | null;
|
|
56
|
+
/**
|
|
57
|
+
* Resolve a HAL worker.* op to C++ against the shared contract.
|
|
58
|
+
* Returns undefined for ops the caller did not lower (unsupported).
|
|
59
|
+
*
|
|
60
|
+
* Note: the worker function itself (fnRef) and its argument (argRef) come from
|
|
61
|
+
* the IR as flat-scalar strings (per the hal-op-ir flat-fields contract). The
|
|
62
|
+
* lowering emits a straight call into __tc_worker_submit / __tc_worker_done.
|
|
63
|
+
*/
|
|
64
|
+
export declare function lowerWorkerOp(op: {
|
|
65
|
+
operation: string;
|
|
66
|
+
}): {
|
|
67
|
+
code?: string;
|
|
68
|
+
expression?: string;
|
|
69
|
+
} | undefined;
|