@celox-sim/celox 0.1.14 → 0.1.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/src/e2e.test.ts CHANGED
@@ -400,7 +400,7 @@ describe("E2E: Simulator.create (backward compat)", () => {
400
400
  {
401
401
  __celox_module: true,
402
402
  name: "Adder",
403
- source: ADDER_SOURCE,
403
+ sources: [{ path: "", content: ADDER_SOURCE }],
404
404
  ports: {
405
405
  clk: { direction: "input", type: "clock", width: 1 },
406
406
  rst: { direction: "input", type: "reset", width: 1 },
@@ -499,9 +499,13 @@ module InitTest (
499
499
  b: input bit<8>,
500
500
  ) {}
501
501
  `;
502
- raw = new addon.NativeSimulatorHandle(source, "InitTest", {
503
- fourState: true,
504
- });
502
+ raw = new addon.NativeSimulatorHandle(
503
+ [{ content: source, path: "" }],
504
+ "InitTest",
505
+ {
506
+ fourState: true,
507
+ },
508
+ );
505
509
  const layout = parseNapiLayout(raw.layoutJson);
506
510
  const buf = raw.sharedMemory().buffer;
507
511
 
@@ -536,9 +540,13 @@ module InitTest (
536
540
  });
537
541
 
538
542
  test("AND: 0 & X = 0 (dominant zero)", () => {
539
- raw = new addon.NativeSimulatorHandle(AND_OR_SOURCE, "AndOr", {
540
- fourState: true,
541
- });
543
+ raw = new addon.NativeSimulatorHandle(
544
+ [{ content: AND_OR_SOURCE, path: "" }],
545
+ "AndOr",
546
+ {
547
+ fourState: true,
548
+ },
549
+ );
542
550
  const layout = parseNapiLayout(raw.layoutJson);
543
551
  const buf = raw.sharedMemory().buffer;
544
552
  const view = new DataView(buf);
@@ -571,9 +579,13 @@ module InitTest (
571
579
  });
572
580
 
573
581
  test("OR: 1 | X = 1 (dominant one)", () => {
574
- raw = new addon.NativeSimulatorHandle(AND_OR_SOURCE, "AndOr", {
575
- fourState: true,
576
- });
582
+ raw = new addon.NativeSimulatorHandle(
583
+ [{ content: AND_OR_SOURCE, path: "" }],
584
+ "AndOr",
585
+ {
586
+ fourState: true,
587
+ },
588
+ );
577
589
  const layout = parseNapiLayout(raw.layoutJson);
578
590
  const buf = raw.sharedMemory().buffer;
579
591
  const view = new DataView(buf);
@@ -599,9 +611,13 @@ module InitTest (
599
611
  });
600
612
 
601
613
  test("logic-to-bit assignment strips X mask", () => {
602
- raw = new addon.NativeSimulatorHandle(LOGIC_BIT_MIX_SOURCE, "LogicBitMix", {
603
- fourState: true,
604
- });
614
+ raw = new addon.NativeSimulatorHandle(
615
+ [{ content: LOGIC_BIT_MIX_SOURCE, path: "" }],
616
+ "LogicBitMix",
617
+ {
618
+ fourState: true,
619
+ },
620
+ );
605
621
  const layout = parseNapiLayout(raw.layoutJson);
606
622
  const buf = raw.sharedMemory().buffer;
607
623
  const view = new DataView(buf);
@@ -620,9 +636,13 @@ module InitTest (
620
636
  });
621
637
 
622
638
  test("bit-to-logic assignment has no X", () => {
623
- raw = new addon.NativeSimulatorHandle(LOGIC_BIT_MIX_SOURCE, "LogicBitMix", {
624
- fourState: true,
625
- });
639
+ raw = new addon.NativeSimulatorHandle(
640
+ [{ content: LOGIC_BIT_MIX_SOURCE, path: "" }],
641
+ "LogicBitMix",
642
+ {
643
+ fourState: true,
644
+ },
645
+ );
626
646
  const layout = parseNapiLayout(raw.layoutJson);
627
647
  const buf = raw.sharedMemory().buffer;
628
648
  const view = new DataView(buf);
@@ -642,9 +662,13 @@ module InitTest (
642
662
  });
643
663
 
644
664
  test("arithmetic with X produces all-X output", () => {
645
- raw = new addon.NativeSimulatorHandle(ADDER_4STATE_SOURCE, "Adder4S", {
646
- fourState: true,
647
- });
665
+ raw = new addon.NativeSimulatorHandle(
666
+ [{ content: ADDER_4STATE_SOURCE, path: "" }],
667
+ "Adder4S",
668
+ {
669
+ fourState: true,
670
+ },
671
+ );
648
672
  const layout = parseNapiLayout(raw.layoutJson);
649
673
  const buf = raw.sharedMemory().buffer;
650
674
  const view = new DataView(buf);
@@ -668,9 +692,13 @@ module InitTest (
668
692
  });
669
693
 
670
694
  test("defined inputs in 4-state mode behave like 2-state", () => {
671
- raw = new addon.NativeSimulatorHandle(ADDER_4STATE_SOURCE, "Adder4S", {
672
- fourState: true,
673
- });
695
+ raw = new addon.NativeSimulatorHandle(
696
+ [{ content: ADDER_4STATE_SOURCE, path: "" }],
697
+ "Adder4S",
698
+ {
699
+ fourState: true,
700
+ },
701
+ );
674
702
  const layout = parseNapiLayout(raw.layoutJson);
675
703
  const buf = raw.sharedMemory().buffer;
676
704
  const view = new DataView(buf);
@@ -694,7 +722,11 @@ module InitTest (
694
722
  });
695
723
 
696
724
  test("FF captures X from input, reset clears X", () => {
697
- raw = new addon.NativeSimulatorHandle(FF_SOURCE, "FF", { fourState: true });
725
+ raw = new addon.NativeSimulatorHandle(
726
+ [{ content: FF_SOURCE, path: "" }],
727
+ "FF",
728
+ { fourState: true },
729
+ );
698
730
  const layout = parseNapiLayout(raw.layoutJson);
699
731
  const buf = raw.sharedMemory().buffer;
700
732
  const view = new DataView(buf);
@@ -744,9 +776,13 @@ module InitTest (
744
776
  });
745
777
 
746
778
  test("FourState write through DUT sets value and mask", () => {
747
- raw = new addon.NativeSimulatorHandle(ADDER_4STATE_SOURCE, "Adder4S", {
748
- fourState: true,
749
- });
779
+ raw = new addon.NativeSimulatorHandle(
780
+ [{ content: ADDER_4STATE_SOURCE, path: "" }],
781
+ "Adder4S",
782
+ {
783
+ fourState: true,
784
+ },
785
+ );
750
786
  const layout = parseNapiLayout(raw.layoutJson);
751
787
  const buf = raw.sharedMemory().buffer;
752
788
  const view = new DataView(buf);
@@ -764,9 +800,13 @@ module InitTest (
764
800
  });
765
801
 
766
802
  test("setting defined value clears X mask", () => {
767
- raw = new addon.NativeSimulatorHandle(ADDER_4STATE_SOURCE, "Adder4S", {
768
- fourState: true,
769
- });
803
+ raw = new addon.NativeSimulatorHandle(
804
+ [{ content: ADDER_4STATE_SOURCE, path: "" }],
805
+ "Adder4S",
806
+ {
807
+ fourState: true,
808
+ },
809
+ );
770
810
  const layout = parseNapiLayout(raw.layoutJson);
771
811
  const buf = raw.sharedMemory().buffer;
772
812
  const view = new DataView(buf);
@@ -866,9 +906,13 @@ describe("E2E: 4-state high-level DUT API", () => {
866
906
 
867
907
  test("multiplexer with X selector produces X output", () => {
868
908
  const addon = loadNativeAddon();
869
- const raw = new addon.NativeSimulatorHandle(MULTIPLEXER_SOURCE, "Mux4", {
870
- fourState: true,
871
- });
909
+ const raw = new addon.NativeSimulatorHandle(
910
+ [{ content: MULTIPLEXER_SOURCE, path: "" }],
911
+ "Mux4",
912
+ {
913
+ fourState: true,
914
+ },
915
+ );
872
916
  const layout = parseNapiLayout(raw.layoutJson);
873
917
  const buf = raw.sharedMemory().buffer;
874
918
  const view = new DataView(buf);
@@ -1676,7 +1720,7 @@ describe("E2E: parameter override — DUT correctness", () => {
1676
1720
  {
1677
1721
  __celox_module: true,
1678
1722
  name: "ParamWidth",
1679
- source: PARAM_WIDTH_SOURCE,
1723
+ sources: [{ path: "", content: PARAM_WIDTH_SOURCE }],
1680
1724
  ports: {
1681
1725
  // STALE: these say width=8, but we'll override to 16
1682
1726
  a: { direction: "input", type: "logic", width: 8 },
@@ -2345,3 +2389,168 @@ describe("E2E: deadStorePolicy option", () => {
2345
2389
  sim.dispose();
2346
2390
  });
2347
2391
  });
2392
+
2393
+ // ---------------------------------------------------------------------------
2394
+ // E2E: Proto package ordering (issue #22)
2395
+ // ---------------------------------------------------------------------------
2396
+
2397
+ const PROTO_PKG_SOURCE = `
2398
+ pub proto package ItemProto {
2399
+ type Item;
2400
+ }
2401
+
2402
+ pub package ItemPlain::<WIDTH: u32 = 16> for ItemProto {
2403
+ type Item = logic<WIDTH>;
2404
+ }
2405
+ `;
2406
+
2407
+ const GENERIC_MODULE_SOURCE = `
2408
+ pub module GenericModule::<PKG: ItemProto> #(
2409
+ param DEPTH: u32 = 4,
2410
+ ) (
2411
+ clk : input clock ,
2412
+ rst : input reset ,
2413
+ d_in : input PKG::Item ,
2414
+ d_out: output PKG::Item ,
2415
+ ) {
2416
+ var store: PKG::Item;
2417
+ always_ff (clk, rst) {
2418
+ if_reset { store = '0; }
2419
+ else { store = d_in; }
2420
+ }
2421
+ assign d_out = store;
2422
+ }
2423
+
2424
+ pub module ConcreteModule #(
2425
+ param DEPTH: u32 = 4,
2426
+ ) (
2427
+ clk : input clock ,
2428
+ rst : input reset ,
2429
+ d_in : input logic<16> ,
2430
+ d_out: output logic<16> ,
2431
+ ) {
2432
+ inst u_inner: GenericModule::<ItemPlain::<16>> #(DEPTH: DEPTH) (
2433
+ clk, rst, d_in, d_out,
2434
+ );
2435
+ }
2436
+ `;
2437
+
2438
+ const UNRELATED_MODULE_SOURCE = `
2439
+ pub module UnrelatedModule (
2440
+ clk : input clock ,
2441
+ rst : input reset ,
2442
+ d_in : input logic<8> ,
2443
+ d_out: output logic<8> ,
2444
+ ) {
2445
+ var data_r: logic<8>;
2446
+ always_ff (clk, rst) {
2447
+ if_reset { data_r = 0; }
2448
+ else { data_r = d_in; }
2449
+ }
2450
+ assign d_out = data_r;
2451
+ }
2452
+ `;
2453
+
2454
+ describe("E2E: Proto package ordering (issue #22)", () => {
2455
+ test("single-file source fails when project has proto packages in wrong order", () => {
2456
+ // Reproduce the original bug: when proto_pkg definitions come after
2457
+ // the code that references them (as a single concatenated source),
2458
+ // the analyzer fails with "referred before it is defined".
2459
+ expect(() =>
2460
+ Simulator.fromSource(
2461
+ GENERIC_MODULE_SOURCE + PROTO_PKG_SOURCE + UNRELATED_MODULE_SOURCE,
2462
+ "ConcreteModule",
2463
+ ),
2464
+ ).toThrow();
2465
+ });
2466
+
2467
+ test("multi-source: unrelated module compiles with proto packages in project", () => {
2468
+ // New behavior: all source files are passed as separate entries.
2469
+ // The Rust from_sources() handles dependency ordering correctly.
2470
+ const sim = Simulator.fromSource<{
2471
+ d_in: bigint;
2472
+ readonly d_out: bigint;
2473
+ }>(
2474
+ PROTO_PKG_SOURCE + GENERIC_MODULE_SOURCE + UNRELATED_MODULE_SOURCE,
2475
+ "UnrelatedModule",
2476
+ );
2477
+
2478
+ // Reset sequence
2479
+ sim.dut.rst = 0n;
2480
+ sim.tick();
2481
+ sim.tick();
2482
+ sim.dut.rst = 1n;
2483
+
2484
+ sim.dut.d_in = 42n;
2485
+ sim.tick();
2486
+ sim.tick();
2487
+
2488
+ expect(sim.dut.d_out).toBe(42n);
2489
+ sim.dispose();
2490
+ });
2491
+
2492
+ test("multi-source: ConcreteModule using proto package works", () => {
2493
+ // The concrete module that actually uses the proto package should
2494
+ // also compile and simulate correctly.
2495
+ const sim = Simulator.fromSource<{
2496
+ d_in: bigint;
2497
+ readonly d_out: bigint;
2498
+ }>(
2499
+ PROTO_PKG_SOURCE + GENERIC_MODULE_SOURCE + UNRELATED_MODULE_SOURCE,
2500
+ "ConcreteModule",
2501
+ );
2502
+
2503
+ // Reset sequence
2504
+ sim.dut.rst = 0n;
2505
+ sim.tick();
2506
+ sim.tick();
2507
+ sim.dut.rst = 1n;
2508
+
2509
+ sim.dut.d_in = 0xabcdn;
2510
+ sim.tick();
2511
+ sim.tick();
2512
+
2513
+ expect(sim.dut.d_out).toBe(0xabcdn);
2514
+ sim.dispose();
2515
+ });
2516
+
2517
+ test("Simulator.create() with multi-source ModuleDefinition", () => {
2518
+ // Simulates what celox-ts-gen now generates: a ModuleDefinition with
2519
+ // sources[] containing all project files.
2520
+ const addon = loadNativeAddon();
2521
+ const nativeCreate = createSimulatorBridge(addon);
2522
+
2523
+ const sim = Simulator.create(
2524
+ {
2525
+ __celox_module: true,
2526
+ name: "UnrelatedModule",
2527
+ sources: [
2528
+ { path: "proto_pkg.veryl", content: PROTO_PKG_SOURCE },
2529
+ { path: "generic_module.veryl", content: GENERIC_MODULE_SOURCE },
2530
+ { path: "unrelated_module.veryl", content: UNRELATED_MODULE_SOURCE },
2531
+ ],
2532
+ ports: {
2533
+ clk: { direction: "input", type: "clock", width: 1 },
2534
+ rst: { direction: "input", type: "reset", width: 1 },
2535
+ d_in: { direction: "input", type: "logic", width: 8 },
2536
+ d_out: { direction: "output", type: "logic", width: 8 },
2537
+ },
2538
+ events: ["clk"],
2539
+ },
2540
+ { __nativeCreate: nativeCreate },
2541
+ );
2542
+
2543
+ // Reset sequence
2544
+ sim.dut.rst = 0n;
2545
+ sim.tick();
2546
+ sim.tick();
2547
+ sim.dut.rst = 1n;
2548
+
2549
+ sim.dut.d_in = 99n;
2550
+ sim.tick();
2551
+ sim.tick();
2552
+
2553
+ expect(sim.dut.d_out).toBe(99n);
2554
+ sim.dispose();
2555
+ });
2556
+ });
package/src/index.ts CHANGED
@@ -51,6 +51,7 @@ export type {
51
51
  PortInfo,
52
52
  SignalLayout,
53
53
  SimulatorOptions,
54
+ SourceFile,
54
55
  TrueLoopSpec,
55
56
  } from "./types.js";
56
57
  // 4-state helpers
@@ -19,6 +19,7 @@ import type {
19
19
  PortInfo,
20
20
  SignalLayout,
21
21
  SimulatorOptions,
22
+ SourceFile,
22
23
  TrueLoopSpec,
23
24
  } from "./types.js";
24
25
 
@@ -30,6 +31,7 @@ export interface RawNapiSimulatorHandle {
30
31
  readonly layoutJson: string;
31
32
  readonly eventsJson: string;
32
33
  readonly hierarchyJson: string;
34
+ readonly warningsJson: string;
33
35
  readonly stableSize: number;
34
36
  readonly totalSize: number;
35
37
  tick(eventId: number): void;
@@ -44,6 +46,7 @@ export interface RawNapiSimulationHandle {
44
46
  readonly layoutJson: string;
45
47
  readonly eventsJson: string;
46
48
  readonly hierarchyJson: string;
49
+ readonly warningsJson: string;
47
50
  readonly stableSize: number;
48
51
  readonly totalSize: number;
49
52
  readonly defaultMaxSteps: number | null;
@@ -98,10 +101,15 @@ export interface NapiOptions {
98
101
  deadStorePolicy?: string;
99
102
  }
100
103
 
104
+ export interface NapiSourceFile {
105
+ content: string;
106
+ path: string;
107
+ }
108
+
101
109
  export interface RawNapiAddon {
102
110
  NativeSimulatorHandle: {
103
111
  new (
104
- code: string,
112
+ sources: NapiSourceFile[],
105
113
  top: string,
106
114
  options?: NapiOptions,
107
115
  ): RawNapiSimulatorHandle;
@@ -113,7 +121,7 @@ export interface RawNapiAddon {
113
121
  };
114
122
  NativeSimulationHandle: {
115
123
  new (
116
- code: string,
124
+ sources: NapiSourceFile[],
117
125
  top: string,
118
126
  options?: NapiOptions,
119
127
  ): RawNapiSimulationHandle;
@@ -265,7 +273,8 @@ export function buildNapiOpts(
265
273
  preserveTopPorts: "preserve_top_ports",
266
274
  preserveAllPorts: "preserve_all_ports",
267
275
  };
268
- napiOpts.deadStorePolicy = map[options.deadStorePolicy] ?? options.deadStorePolicy;
276
+ napiOpts.deadStorePolicy =
277
+ map[options.deadStorePolicy] ?? options.deadStorePolicy;
269
278
  hasOpt = true;
270
279
  }
271
280
 
@@ -611,12 +620,20 @@ function parseLegacyLayout(json: string): Record<string, SignalLayout> {
611
620
  */
612
621
  export function createSimulatorBridge(addon: RawNapiAddon): NativeCreateFn {
613
622
  return (
614
- source: string,
623
+ sources: ReadonlyArray<SourceFile>,
615
624
  moduleName: string,
616
625
  options: SimulatorOptions,
617
626
  ): CreateResult<NativeSimulatorHandle> => {
618
627
  const napiOpts = buildNapiOpts(options);
619
- const raw = new addon.NativeSimulatorHandle(source, moduleName, napiOpts);
628
+ const napiSources = sources.map((s) => ({
629
+ content: s.content,
630
+ path: s.path,
631
+ }));
632
+ const raw = new addon.NativeSimulatorHandle(
633
+ napiSources,
634
+ moduleName,
635
+ napiOpts,
636
+ );
620
637
 
621
638
  const layout = parseLegacyLayout(raw.layoutJson);
622
639
  const events: Record<string, number> = JSON.parse(raw.eventsJson);
@@ -625,7 +642,9 @@ export function createSimulatorBridge(addon: RawNapiAddon): NativeCreateFn {
625
642
  const buf = raw.sharedMemory().buffer;
626
643
  const handle = wrapDirectSimulatorHandle(raw);
627
644
 
628
- return { buffer: buf, layout, events, handle, hierarchy };
645
+ const warnings: string[] = JSON.parse(raw.warningsJson ?? "[]");
646
+
647
+ return { buffer: buf, layout, events, handle, hierarchy, warnings };
629
648
  };
630
649
  }
631
650
 
@@ -637,12 +656,20 @@ export function createSimulationBridge(
637
656
  addon: RawNapiAddon,
638
657
  ): NativeCreateSimulationFn {
639
658
  return (
640
- source: string,
659
+ sources: ReadonlyArray<SourceFile>,
641
660
  moduleName: string,
642
661
  options: SimulatorOptions,
643
662
  ): CreateResult<NativeSimulationHandle> => {
644
663
  const napiOpts = buildNapiOpts(options);
645
- const raw = new addon.NativeSimulationHandle(source, moduleName, napiOpts);
664
+ const napiSources = sources.map((s) => ({
665
+ content: s.content,
666
+ path: s.path,
667
+ }));
668
+ const raw = new addon.NativeSimulationHandle(
669
+ napiSources,
670
+ moduleName,
671
+ napiOpts,
672
+ );
646
673
 
647
674
  const layout = parseLegacyLayout(raw.layoutJson);
648
675
  const events: Record<string, number> = JSON.parse(raw.eventsJson);
@@ -650,7 +677,8 @@ export function createSimulationBridge(
650
677
 
651
678
  const buf = raw.sharedMemory().buffer;
652
679
  const handle = wrapDirectSimulationHandle(raw);
680
+ const warnings: string[] = JSON.parse(raw.warningsJson ?? "[]");
653
681
 
654
- return { buffer: buf, layout, events, handle, hierarchy };
682
+ return { buffer: buf, layout, events, handle, hierarchy, warnings };
655
683
  };
656
684
  }
@@ -20,7 +20,7 @@ interface TopPorts {
20
20
  const TopModule: ModuleDefinition<TopPorts> = {
21
21
  __celox_module: true,
22
22
  name: "Top",
23
- source: "module Top ...",
23
+ sources: [{ path: "", content: "module Top ..." }],
24
24
  ports: {
25
25
  clk: { direction: "input", type: "clock", width: 1 },
26
26
  rst: { direction: "input", type: "reset", width: 1 },
package/src/simulation.ts CHANGED
@@ -22,6 +22,7 @@ import type {
22
22
  NativeSimulationHandle,
23
23
  SignalLayout,
24
24
  SimulatorOptions,
25
+ SourceFile,
25
26
  } from "./types.js";
26
27
  import { SimulationTimeoutError } from "./types.js";
27
28
 
@@ -31,7 +32,7 @@ import { SimulationTimeoutError } from "./types.js";
31
32
  * @internal
32
33
  */
33
34
  export type NativeCreateSimulationFn = (
34
- source: string,
35
+ sources: ReadonlyArray<SourceFile>,
35
36
  moduleName: string,
36
37
  options: SimulatorOptions,
37
38
  ) => CreateResult<NativeSimulationHandle>;
@@ -65,6 +66,7 @@ export class Simulation<P = Record<string, unknown>> {
65
66
  { period: number; eventId: number }
66
67
  >();
67
68
  private readonly _defaultMaxSteps: number | undefined;
69
+ private readonly _warnings: readonly string[];
68
70
  private _disposed = false;
69
71
 
70
72
  private constructor(
@@ -77,6 +79,7 @@ export class Simulation<P = Record<string, unknown>> {
77
79
  string,
78
80
  SignalLayout & { typeKind?: string; associatedClock?: string }
79
81
  >,
82
+ warnings: string[],
80
83
  defaultMaxSteps?: number,
81
84
  ) {
82
85
  this._handle = handle;
@@ -85,6 +88,7 @@ export class Simulation<P = Record<string, unknown>> {
85
88
  this._state = state;
86
89
  this._buffer = buffer;
87
90
  this._layout = layout;
91
+ this._warnings = warnings;
88
92
  this._defaultMaxSteps = defaultMaxSteps;
89
93
  }
90
94
 
@@ -107,11 +111,7 @@ export class Simulation<P = Record<string, unknown>> {
107
111
 
108
112
  // When the module was produced by the Vite plugin, delegate to fromProject()
109
113
  if (module.projectPath && !merged?.__nativeCreate) {
110
- return Simulation.fromProject<P>(
111
- module.projectPath,
112
- module.name,
113
- merged,
114
- );
114
+ return Simulation.fromProject<P>(module.projectPath, module.name, merged);
115
115
  }
116
116
 
117
117
  const createFn = merged?.__nativeCreate ?? _nativeCreate;
@@ -133,7 +133,7 @@ export class Simulation<P = Record<string, unknown>> {
133
133
  parameters,
134
134
  deadStorePolicy,
135
135
  } = merged ?? {};
136
- const result = createFn(module.source, module.name, {
136
+ const result = createFn(module.sources, module.name, {
137
137
  fourState,
138
138
  vcd,
139
139
  optimize,
@@ -167,6 +167,7 @@ export class Simulation<P = Record<string, unknown>> {
167
167
  state,
168
168
  result.buffer,
169
169
  result.layout,
170
+ result.warnings ?? [],
170
171
  );
171
172
  }
172
173
 
@@ -189,12 +190,19 @@ export class Simulation<P = Record<string, unknown>> {
189
190
  ): Simulation<P> {
190
191
  const addon = loadNativeAddon(options?.nativeAddonPath);
191
192
  const napiOpts = buildNapiOpts(options);
192
- const raw = new addon.NativeSimulationHandle(source, top, napiOpts);
193
+ const raw = new addon.NativeSimulationHandle(
194
+ [{ content: source, path: "" }],
195
+ top,
196
+ napiOpts,
197
+ );
193
198
 
194
199
  const layout = parseNapiLayout(raw.layoutJson);
195
200
  const events: Record<string, number> = JSON.parse(raw.eventsJson);
196
201
  const rawHierarchy = parseHierarchyLayout(raw.hierarchyJson, events);
197
- const hierarchy = filterHierarchyForDse(rawHierarchy, options?.deadStorePolicy);
202
+ const hierarchy = filterHierarchyForDse(
203
+ rawHierarchy,
204
+ options?.deadStorePolicy,
205
+ );
198
206
 
199
207
  const ports = buildPortsFromLayout(hierarchy.signals, events);
200
208
 
@@ -211,7 +219,17 @@ export class Simulation<P = Record<string, unknown>> {
211
219
  hierarchy,
212
220
  );
213
221
 
214
- return new Simulation<P>(handle, dut, events, state, buf, layout.signals);
222
+ const warnings: string[] = JSON.parse(raw.warningsJson ?? "[]");
223
+
224
+ return new Simulation<P>(
225
+ handle,
226
+ dut,
227
+ events,
228
+ state,
229
+ buf,
230
+ layout.signals,
231
+ warnings,
232
+ );
215
233
  }
216
234
 
217
235
  /**
@@ -242,7 +260,10 @@ export class Simulation<P = Record<string, unknown>> {
242
260
  const layout = parseNapiLayout(raw.layoutJson);
243
261
  const events: Record<string, number> = JSON.parse(raw.eventsJson);
244
262
  const rawHierarchy = parseHierarchyLayout(raw.hierarchyJson, events);
245
- const hierarchy = filterHierarchyForDse(rawHierarchy, options?.deadStorePolicy);
263
+ const hierarchy = filterHierarchyForDse(
264
+ rawHierarchy,
265
+ options?.deadStorePolicy,
266
+ );
246
267
 
247
268
  const ports = buildPortsFromLayout(hierarchy.signals, events);
248
269
 
@@ -260,6 +281,8 @@ export class Simulation<P = Record<string, unknown>> {
260
281
  hierarchy,
261
282
  );
262
283
 
284
+ const warnings: string[] = JSON.parse(raw.warningsJson ?? "[]");
285
+
263
286
  return new Simulation<P>(
264
287
  handle,
265
288
  dut,
@@ -267,6 +290,7 @@ export class Simulation<P = Record<string, unknown>> {
267
290
  state,
268
291
  buf,
269
292
  layout.signals,
293
+ warnings,
270
294
  defaultMaxSteps,
271
295
  );
272
296
  }
@@ -276,6 +300,11 @@ export class Simulation<P = Record<string, unknown>> {
276
300
  return this._dut;
277
301
  }
278
302
 
303
+ /** Analyzer warnings emitted during compilation. */
304
+ get warnings(): readonly string[] {
305
+ return this._warnings;
306
+ }
307
+
279
308
  /**
280
309
  * Register a periodic clock.
281
310
  *
@@ -20,7 +20,7 @@ interface AdderPorts {
20
20
  const AdderModule: ModuleDefinition<AdderPorts> = {
21
21
  __celox_module: true,
22
22
  name: "Adder",
23
- source: "module Adder ...",
23
+ sources: [{ path: "", content: "module Adder ..." }],
24
24
  ports: {
25
25
  clk: { direction: "input", type: "clock", width: 1 },
26
26
  rst: { direction: "input", type: "reset", width: 1 },