@celox-sim/celox 0.1.5 → 0.1.6

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/types.d.ts CHANGED
@@ -47,6 +47,10 @@ export interface SignalLayout {
47
47
  /** If true, an equal-sized mask follows immediately after the value. */
48
48
  readonly is4state: boolean;
49
49
  readonly direction: "input" | "output" | "inout";
50
+ /** The Veryl type kind (e.g. "clock", "reset_async_high", "logic"). */
51
+ readonly typeKind?: string;
52
+ /** For reset signals, the name of the associated clock (from FfDeclaration). */
53
+ readonly associatedClock?: string;
50
54
  }
51
55
  /**
52
56
  * Opaque handle returned by NAPI for event-based simulation.
@@ -69,6 +73,7 @@ export interface NativeSimulationHandle {
69
73
  runUntil(endTime: number): void;
70
74
  step(): number | null;
71
75
  time(): number;
76
+ nextEventTime(): number | null;
72
77
  evalComb(): void;
73
78
  dump(timestamp: number): void;
74
79
  dispose(): void;
@@ -91,12 +96,24 @@ export interface CreateResult<H extends NativeHandle = NativeHandle> {
91
96
  readonly events: Record<string, number>;
92
97
  /** Native control handle. */
93
98
  readonly handle: H;
99
+ /** Full instance hierarchy (optional — present when NAPI provides it). */
100
+ readonly hierarchy?: import("./napi-helpers.js").HierarchyNode;
94
101
  }
95
102
  export interface SimulatorOptions {
96
103
  /** Enable 4-state (X/Z) simulation. Default: false. */
97
104
  fourState?: boolean;
98
105
  /** Path to write VCD waveform output. */
99
106
  vcd?: string;
107
+ /** Enable Cranelift optimization passes. */
108
+ optimize?: boolean;
109
+ /** False-loop declarations to ignore during compilation. */
110
+ falseLoops?: LoopBreak[];
111
+ /** True-loop declarations with convergence limits. */
112
+ trueLoops?: TrueLoopSpec[];
113
+ /** Clock polarity. Default: "posedge". */
114
+ clockType?: "posedge" | "negedge";
115
+ /** Reset type. Default: "async_low". */
116
+ resetType?: "async_high" | "async_low" | "sync_high" | "sync_low";
100
117
  }
101
118
  /** A resolved event reference for use with `tick()`. */
102
119
  export interface EventHandle {
@@ -114,4 +131,21 @@ export interface FourStateValue {
114
131
  /** Construct a 4-state value. Mask bits set to 1 indicate X. */
115
132
  export declare function FourState(value: number | bigint, mask: number | bigint): FourStateValue;
116
133
  export declare function isFourStateValue(v: unknown): v is FourStateValue;
134
+ /**
135
+ * Thrown when a simulation helper exceeds its step budget.
136
+ */
137
+ export declare class SimulationTimeoutError extends Error {
138
+ readonly time: number;
139
+ readonly steps: number;
140
+ constructor(message: string, time: number, steps: number);
141
+ }
142
+ /** Specifies a false-loop to ignore during compilation. */
143
+ export interface LoopBreak {
144
+ from: string;
145
+ to: string;
146
+ }
147
+ /** Specifies a true-loop with a convergence iteration limit. */
148
+ export interface TrueLoopSpec extends LoopBreak {
149
+ maxIter: number;
150
+ }
117
151
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js CHANGED
@@ -20,4 +20,20 @@ export function isFourStateValue(v) {
20
20
  v !== null &&
21
21
  v.__fourState === true);
22
22
  }
23
+ // ---------------------------------------------------------------------------
24
+ // Simulation timeout error
25
+ // ---------------------------------------------------------------------------
26
+ /**
27
+ * Thrown when a simulation helper exceeds its step budget.
28
+ */
29
+ export class SimulationTimeoutError extends Error {
30
+ time;
31
+ steps;
32
+ constructor(message, time, steps) {
33
+ super(message);
34
+ this.name = "SimulationTimeoutError";
35
+ this.time = time;
36
+ this.steps = steps;
37
+ }
38
+ }
23
39
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgIH,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,mCAAmC;AACnC,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AASvC,gEAAgE;AAChE,MAAM,UAAU,SAAS,CACvB,KAAsB,EACtB,IAAqB;IAErB,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,CAAU;IACzC,OAAO,CACL,OAAO,CAAC,KAAK,QAAQ;QACrB,CAAC,KAAK,IAAI;QACT,CAAoB,CAAC,WAAW,KAAK,IAAI,CAC3C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiJH,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,mCAAmC;AACnC,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AASvC,gEAAgE;AAChE,MAAM,UAAU,SAAS,CACvB,KAAsB,EACtB,IAAqB;IAErB,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,CAAU;IACzC,OAAO,CACL,OAAO,CAAC,KAAK,QAAQ;QACrB,CAAC,KAAK,IAAI;QACT,CAAoB,CAAC,WAAW,KAAK,IAAI,CAC3C,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E;;GAEG;AACH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACtC,IAAI,CAAS;IACb,KAAK,CAAS;IAEvB,YAAY,OAAe,EAAE,IAAY,EAAE,KAAa;QACtD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celox-sim/celox",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "TypeScript runtime for Celox HDL simulation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "license": "MIT",
31
31
  "dependencies": {
32
- "@celox-sim/celox-napi": "0.1.5"
32
+ "@celox-sim/celox-napi": "0.1.6"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "vitest": ">=1.0.0"
package/src/dut.ts CHANGED
@@ -13,6 +13,7 @@ import type {
13
13
  SignalLayout,
14
14
  FourStateValue,
15
15
  } from "./types.js";
16
+ import type { HierarchyNode } from "./napi-helpers.js";
16
17
  import { isFourStateValue } from "./types.js";
17
18
 
18
19
  // ---------------------------------------------------------------------------
@@ -183,11 +184,12 @@ function writeAllX(view: DataView, sig: SignalLayout): void {
183
184
  /**
184
185
  * Create a DUT accessor object with defineProperty-based getters/setters.
185
186
  *
186
- * @param buffer SharedArrayBuffer from NAPI create()
187
- * @param layout Per-signal byte layout within the buffer
188
- * @param portDefs Port metadata from the ModuleDefinition
189
- * @param handle Native handle (for evalComb calls)
190
- * @param state Shared dirty-tracking state
187
+ * @param buffer SharedArrayBuffer from NAPI create()
188
+ * @param layout Per-signal byte layout within the buffer
189
+ * @param portDefs Port metadata from the ModuleDefinition
190
+ * @param handle Native handle (for evalComb calls)
191
+ * @param state Shared dirty-tracking state
192
+ * @param hierarchy Optional hierarchy node for child instance access
191
193
  */
192
194
  export function createDut<P>(
193
195
  buffer: ArrayBuffer | SharedArrayBuffer,
@@ -195,6 +197,7 @@ export function createDut<P>(
195
197
  portDefs: Record<string, PortInfo>,
196
198
  handle: NativeHandle,
197
199
  state: DirtyState,
200
+ hierarchy?: HierarchyNode,
198
201
  ): P {
199
202
  const view = new DataView(buffer);
200
203
  const obj = Object.create(null) as P;
@@ -243,6 +246,91 @@ export function createDut<P>(
243
246
  defineSignalProperty(obj as object, name, view, sig, port, handle, state);
244
247
  }
245
248
 
249
+ // Attach child instance accessors from hierarchy
250
+ if (hierarchy) {
251
+ for (const [childName, instances] of Object.entries(hierarchy.children)) {
252
+ if (instances.length === 1) {
253
+ const childDut = createChildDut(buffer, instances[0]!, handle, state);
254
+ Object.defineProperty(obj, childName, {
255
+ value: childDut,
256
+ enumerable: true,
257
+ configurable: false,
258
+ writable: false,
259
+ });
260
+ } else if (instances.length > 1) {
261
+ const childDuts = instances.map((inst) =>
262
+ createChildDut(buffer, inst, handle, state),
263
+ );
264
+ Object.defineProperty(obj, childName, {
265
+ value: childDuts,
266
+ enumerable: true,
267
+ configurable: false,
268
+ writable: false,
269
+ });
270
+ }
271
+ }
272
+ }
273
+
274
+ return obj;
275
+ }
276
+
277
+ /**
278
+ * Create a child instance DUT accessor from a HierarchyNode.
279
+ * Recursively creates accessors for the child's signals and its own children.
280
+ */
281
+ export function createChildDut(
282
+ buffer: ArrayBuffer | SharedArrayBuffer,
283
+ hierarchy: HierarchyNode,
284
+ handle: NativeHandle,
285
+ state: DirtyState,
286
+ ): object {
287
+ const view = new DataView(buffer);
288
+ const obj = Object.create(null);
289
+
290
+ // Define signal properties for this child instance
291
+ for (const [name, port] of Object.entries(hierarchy.ports)) {
292
+ if (port.type === "clock") continue;
293
+
294
+ const sig = hierarchy.forDut[name];
295
+ if (!sig) continue;
296
+
297
+ if (port.arrayDims && port.arrayDims.length > 0) {
298
+ const arrayObj = createArrayDut(view, sig, port, handle, state);
299
+ Object.defineProperty(obj, name, {
300
+ value: arrayObj,
301
+ enumerable: true,
302
+ configurable: false,
303
+ writable: false,
304
+ });
305
+ continue;
306
+ }
307
+
308
+ defineSignalProperty(obj, name, view, sig, port, handle, state);
309
+ }
310
+
311
+ // Recursively attach children
312
+ for (const [childName, instances] of Object.entries(hierarchy.children)) {
313
+ if (instances.length === 1) {
314
+ const childDut = createChildDut(buffer, instances[0]!, handle, state);
315
+ Object.defineProperty(obj, childName, {
316
+ value: childDut,
317
+ enumerable: true,
318
+ configurable: false,
319
+ writable: false,
320
+ });
321
+ } else if (instances.length > 1) {
322
+ const childDuts = instances.map((inst) =>
323
+ createChildDut(buffer, inst, handle, state),
324
+ );
325
+ Object.defineProperty(obj, childName, {
326
+ value: childDuts,
327
+ enumerable: true,
328
+ configurable: false,
329
+ writable: false,
330
+ });
331
+ }
332
+ }
333
+
246
334
  return obj;
247
335
  }
248
336
 
package/src/e2e.bench.ts CHANGED
@@ -12,7 +12,7 @@
12
12
  import { bench, describe, afterAll } from "vitest";
13
13
  import { Simulator } from "./simulator.js";
14
14
  import { Simulation } from "./simulation.js";
15
- import type { ModuleDefinition } from "./types.js";
15
+ import type { ModuleDefinition, SimulationTimeoutError } from "./types.js";
16
16
  import {
17
17
  loadNativeAddon,
18
18
  createSimulatorBridge,
@@ -238,3 +238,166 @@ describe("simulation-time-based", () => {
238
238
  { iterations: 3, time: 0 },
239
239
  );
240
240
  });
241
+
242
+ /**
243
+ * Phase 3b: Testbench helpers benchmarks.
244
+ *
245
+ * Compares waitForCycles vs manual step loop, and runUntil with/without
246
+ * maxSteps guard to measure overhead.
247
+ */
248
+ describe("testbench-helpers", () => {
249
+ const COUNTER_CODE = `
250
+ module Counter (
251
+ clk: input clock,
252
+ rst: input reset,
253
+ en: input logic,
254
+ count: output logic<8>,
255
+ ) {
256
+ var count_r: logic<8>;
257
+
258
+ always_ff (clk, rst) {
259
+ if_reset {
260
+ count_r = 0;
261
+ } else if en {
262
+ count_r = count_r + 1;
263
+ }
264
+ }
265
+
266
+ always_comb {
267
+ count = count_r;
268
+ }
269
+ }
270
+ `;
271
+
272
+ interface CounterPorts {
273
+ rst: number;
274
+ en: number;
275
+ readonly count: number;
276
+ }
277
+
278
+ // waitForCycles benchmark
279
+ const simWait = Simulation.fromSource<CounterPorts>(COUNTER_CODE, "Counter");
280
+ simWait.addClock("clk", { period: 10 });
281
+ simWait.dut.rst = 1;
282
+ simWait.runUntil(20);
283
+ simWait.dut.rst = 0;
284
+ simWait.dut.en = 1;
285
+
286
+ afterAll(() => {
287
+ simWait.dispose();
288
+ });
289
+
290
+ bench(
291
+ "waitForCycles_x1000",
292
+ () => {
293
+ simWait.waitForCycles("clk", 1000);
294
+ },
295
+ { iterations: 3, time: 0 },
296
+ );
297
+
298
+ bench(
299
+ "manual_step_loop_x2000",
300
+ () => {
301
+ for (let i = 0; i < 2000; i++) {
302
+ simWait.step();
303
+ }
304
+ },
305
+ { iterations: 3, time: 0 },
306
+ );
307
+
308
+ // runUntil: fast Rust path vs guarded TS path
309
+ const simRun = Simulation.fromSource<CounterPorts>(COUNTER_CODE, "Counter");
310
+ simRun.addClock("clk", { period: 10 });
311
+ simRun.dut.rst = 1;
312
+ simRun.runUntil(20);
313
+ simRun.dut.rst = 0;
314
+ simRun.dut.en = 1;
315
+
316
+ afterAll(() => {
317
+ simRun.dispose();
318
+ });
319
+
320
+ bench(
321
+ "runUntil_fast_path_100000",
322
+ () => {
323
+ const base = simRun.time();
324
+ simRun.runUntil(base + 100_000);
325
+ },
326
+ { iterations: 3, time: 0 },
327
+ );
328
+
329
+ bench(
330
+ "runUntil_guarded_100000",
331
+ () => {
332
+ const base = simRun.time();
333
+ simRun.runUntil(base + 100_000, { maxSteps: 1_000_000 });
334
+ },
335
+ { iterations: 3, time: 0 },
336
+ );
337
+ });
338
+
339
+ /**
340
+ * Phase 3c: Optimize flag benchmarks.
341
+ *
342
+ * Compares build time and tick performance with and without optimization.
343
+ */
344
+ describe("optimize-flag", () => {
345
+ bench(
346
+ "build_without_optimize",
347
+ () => {
348
+ const sim = Simulator.fromSource<TopPorts>(CODE, "Top");
349
+ sim.dispose();
350
+ },
351
+ { iterations: 3, time: 0 },
352
+ );
353
+
354
+ bench(
355
+ "build_with_optimize",
356
+ () => {
357
+ const sim = Simulator.fromSource<TopPorts>(CODE, "Top", {
358
+ optimize: true,
359
+ });
360
+ sim.dispose();
361
+ },
362
+ { iterations: 3, time: 0 },
363
+ );
364
+
365
+ const simNoOpt = Simulator.fromSource<TopPorts>(CODE, "Top");
366
+ simNoOpt.dut.rst = 1;
367
+ simNoOpt.tick();
368
+ simNoOpt.dut.rst = 0;
369
+ simNoOpt.tick();
370
+
371
+ const simOpt = Simulator.fromSource<TopPorts>(CODE, "Top", {
372
+ optimize: true,
373
+ });
374
+ simOpt.dut.rst = 1;
375
+ simOpt.tick();
376
+ simOpt.dut.rst = 0;
377
+ simOpt.tick();
378
+
379
+ afterAll(() => {
380
+ simNoOpt.dispose();
381
+ simOpt.dispose();
382
+ });
383
+
384
+ bench(
385
+ "tick_x10000_without_optimize",
386
+ () => {
387
+ for (let i = 0; i < 10_000; i++) {
388
+ simNoOpt.tick();
389
+ }
390
+ },
391
+ { iterations: 3, time: 0 },
392
+ );
393
+
394
+ bench(
395
+ "tick_x10000_with_optimize",
396
+ () => {
397
+ for (let i = 0; i < 10_000; i++) {
398
+ simOpt.tick();
399
+ }
400
+ },
401
+ { iterations: 3, time: 0 },
402
+ );
403
+ });