@celox-sim/celox 0.1.5 → 0.1.7

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/simulator.ts CHANGED
@@ -8,16 +8,20 @@
8
8
  import type {
9
9
  CreateResult,
10
10
  EventHandle,
11
+ FourStateValue,
11
12
  ModuleDefinition,
12
13
  NativeSimulatorHandle,
14
+ SignalLayout,
13
15
  SimulatorOptions,
14
16
  } from "./types.js";
15
- import { createDut, type DirtyState } from "./dut.js";
17
+ import { createDut, readFourState, type DirtyState } from "./dut.js";
16
18
  import {
17
19
  loadNativeAddon,
18
20
  parseNapiLayout,
21
+ parseHierarchyLayout,
19
22
  buildPortsFromLayout,
20
23
  wrapDirectSimulatorHandle,
24
+ buildNapiOpts,
21
25
  } from "./napi-helpers.js";
22
26
 
23
27
  /**
@@ -54,6 +58,8 @@ export class Simulator<P = Record<string, unknown>> {
54
58
  private readonly _events: Record<string, number>;
55
59
  private readonly _defaultEventId: number;
56
60
  private readonly _state: DirtyState;
61
+ private readonly _buffer: ArrayBuffer | SharedArrayBuffer;
62
+ private readonly _layout: Record<string, SignalLayout>;
57
63
  private _disposed = false;
58
64
 
59
65
  private constructor(
@@ -61,11 +67,15 @@ export class Simulator<P = Record<string, unknown>> {
61
67
  dut: P,
62
68
  events: Record<string, number>,
63
69
  state: DirtyState,
70
+ buffer: ArrayBuffer | SharedArrayBuffer,
71
+ layout: Record<string, SignalLayout>,
64
72
  ) {
65
73
  this._handle = handle;
66
74
  this._dut = dut;
67
75
  this._events = events;
68
76
  this._state = state;
77
+ this._buffer = buffer;
78
+ this._layout = layout;
69
79
  const keys = Object.keys(events);
70
80
  this._defaultEventId = keys.length > 0 ? events[keys[0]!]! : -1;
71
81
  }
@@ -98,8 +108,8 @@ export class Simulator<P = Record<string, unknown>> {
98
108
  );
99
109
  }
100
110
 
101
- const { fourState, vcd } = options ?? {};
102
- const result = createFn(module.source, module.name, { fourState, vcd });
111
+ const { fourState, vcd, optimize, falseLoops, trueLoops, clockType, resetType } = options ?? {};
112
+ const result = createFn(module.source, module.name, { fourState, vcd, optimize, falseLoops, trueLoops, clockType, resetType });
103
113
  const state: DirtyState = { dirty: false };
104
114
 
105
115
  const dut = createDut<P>(
@@ -108,9 +118,10 @@ export class Simulator<P = Record<string, unknown>> {
108
118
  module.ports,
109
119
  result.handle,
110
120
  state,
121
+ result.hierarchy,
111
122
  );
112
123
 
113
- return new Simulator<P>(result.handle, dut, result.events, state);
124
+ return new Simulator<P>(result.handle, dut, result.events, state, result.buffer, result.layout);
114
125
  }
115
126
 
116
127
  /**
@@ -133,11 +144,12 @@ export class Simulator<P = Record<string, unknown>> {
133
144
  options?: SimulatorOptions & { nativeAddonPath?: string },
134
145
  ): Simulator<P> {
135
146
  const addon = loadNativeAddon(options?.nativeAddonPath);
136
- const napiOpts = options?.fourState ? { fourState: options.fourState } : undefined;
147
+ const napiOpts = buildNapiOpts(options);
137
148
  const raw = new addon.NativeSimulatorHandle(source, top, napiOpts);
138
149
 
139
150
  const layout = parseNapiLayout(raw.layoutJson);
140
151
  const events: Record<string, number> = JSON.parse(raw.eventsJson);
152
+ const hierarchy = parseHierarchyLayout(raw.hierarchyJson, events);
141
153
 
142
154
  const ports = buildPortsFromLayout(layout.signals, events);
143
155
 
@@ -145,9 +157,9 @@ export class Simulator<P = Record<string, unknown>> {
145
157
 
146
158
  const state: DirtyState = { dirty: false };
147
159
  const handle = wrapDirectSimulatorHandle(raw);
148
- const dut = createDut<P>(buf, layout.forDut, ports, handle, state);
160
+ const dut = createDut<P>(buf, layout.forDut, ports, handle, state, hierarchy);
149
161
 
150
- return new Simulator<P>(handle, dut, events, state);
162
+ return new Simulator<P>(handle, dut, events, state, buf, layout.forDut);
151
163
  }
152
164
 
153
165
  /**
@@ -167,11 +179,12 @@ export class Simulator<P = Record<string, unknown>> {
167
179
  options?: SimulatorOptions & { nativeAddonPath?: string },
168
180
  ): Simulator<P> {
169
181
  const addon = loadNativeAddon(options?.nativeAddonPath);
170
- const napiOpts = options?.fourState ? { fourState: options.fourState } : undefined;
182
+ const napiOpts = buildNapiOpts(options);
171
183
  const raw = addon.NativeSimulatorHandle.fromProject(projectPath, top, napiOpts);
172
184
 
173
185
  const layout = parseNapiLayout(raw.layoutJson);
174
186
  const events: Record<string, number> = JSON.parse(raw.eventsJson);
187
+ const hierarchy = parseHierarchyLayout(raw.hierarchyJson, events);
175
188
 
176
189
  const ports = buildPortsFromLayout(layout.signals, events);
177
190
 
@@ -179,9 +192,9 @@ export class Simulator<P = Record<string, unknown>> {
179
192
 
180
193
  const state: DirtyState = { dirty: false };
181
194
  const handle = wrapDirectSimulatorHandle(raw);
182
- const dut = createDut<P>(buf, layout.forDut, ports, handle, state);
195
+ const dut = createDut<P>(buf, layout.forDut, ports, handle, state, hierarchy);
183
196
 
184
- return new Simulator<P>(handle, dut, events, state);
197
+ return new Simulator<P>(handle, dut, events, state, buf, layout.forDut);
185
198
  }
186
199
 
187
200
  /** The DUT accessor object — read/write ports as plain properties. */
@@ -221,12 +234,16 @@ export class Simulator<P = Record<string, unknown>> {
221
234
  ticks = 1;
222
235
  }
223
236
 
237
+ if (this._state.dirty) {
238
+ this._handle.evalComb();
239
+ this._state.dirty = false;
240
+ }
241
+
224
242
  if (ticks === 1) {
225
243
  this._handle.tick(eventId);
226
244
  } else if (ticks > 1) {
227
245
  this._handle.tickN(eventId, ticks);
228
246
  }
229
- this._state.dirty = false;
230
247
  }
231
248
 
232
249
  /** Resolve an event name to a handle for use with `tick()`. */
@@ -240,6 +257,21 @@ export class Simulator<P = Record<string, unknown>> {
240
257
  return { name, id };
241
258
  }
242
259
 
260
+ /**
261
+ * Read the raw 4-state (value + mask) pair for the named port.
262
+ */
263
+ fourState(portName: string): FourStateValue {
264
+ this.ensureAlive();
265
+ const sig = this._layout[portName];
266
+ if (!sig) {
267
+ throw new Error(
268
+ `Unknown port '${portName}'. Available: ${Object.keys(this._layout).join(", ")}`,
269
+ );
270
+ }
271
+ const [value, mask] = readFourState(this._buffer, sig);
272
+ return { __fourState: true, value, mask };
273
+ }
274
+
243
275
  /** Write current signal values to VCD at the given timestamp. */
244
276
  dump(timestamp: number): void {
245
277
  this.ensureAlive();
package/src/types.ts CHANGED
@@ -58,6 +58,10 @@ export interface SignalLayout {
58
58
  /** If true, an equal-sized mask follows immediately after the value. */
59
59
  readonly is4state: boolean;
60
60
  readonly direction: "input" | "output" | "inout";
61
+ /** The Veryl type kind (e.g. "clock", "reset_async_high", "logic"). */
62
+ readonly typeKind?: string;
63
+ /** For reset signals, the name of the associated clock (from FfDeclaration). */
64
+ readonly associatedClock?: string;
61
65
  }
62
66
 
63
67
  // ---------------------------------------------------------------------------
@@ -86,6 +90,7 @@ export interface NativeSimulationHandle {
86
90
  runUntil(endTime: number): void;
87
91
  step(): number | null;
88
92
  time(): number;
93
+ nextEventTime(): number | null;
89
94
  evalComb(): void;
90
95
  dump(timestamp: number): void;
91
96
  dispose(): void;
@@ -110,6 +115,8 @@ export interface CreateResult<H extends NativeHandle = NativeHandle> {
110
115
  readonly events: Record<string, number>;
111
116
  /** Native control handle. */
112
117
  readonly handle: H;
118
+ /** Full instance hierarchy (optional — present when NAPI provides it). */
119
+ readonly hierarchy?: import("./napi-helpers.js").HierarchyNode;
113
120
  }
114
121
 
115
122
  // ---------------------------------------------------------------------------
@@ -121,6 +128,16 @@ export interface SimulatorOptions {
121
128
  fourState?: boolean;
122
129
  /** Path to write VCD waveform output. */
123
130
  vcd?: string;
131
+ /** Enable Cranelift optimization passes. */
132
+ optimize?: boolean;
133
+ /** False-loop declarations to ignore during compilation. */
134
+ falseLoops?: LoopBreak[];
135
+ /** True-loop declarations with convergence limits. */
136
+ trueLoops?: TrueLoopSpec[];
137
+ /** Clock polarity. Default: "posedge". */
138
+ clockType?: "posedge" | "negedge";
139
+ /** Reset type. Default: "async_low". */
140
+ resetType?: "async_high" | "async_low" | "sync_high" | "sync_low";
124
141
  }
125
142
 
126
143
  // ---------------------------------------------------------------------------
@@ -143,8 +160,8 @@ export const X = Symbol.for("veryl:X");
143
160
  /** A 4-state value with explicit bit-level mask. */
144
161
  export interface FourStateValue {
145
162
  readonly __fourState: true;
146
- readonly value: number | bigint;
147
- readonly mask: number | bigint;
163
+ readonly value: bigint;
164
+ readonly mask: bigint;
148
165
  }
149
166
 
150
167
  /** Construct a 4-state value. Mask bits set to 1 indicate X. */
@@ -152,7 +169,7 @@ export function FourState(
152
169
  value: number | bigint,
153
170
  mask: number | bigint,
154
171
  ): FourStateValue {
155
- return { __fourState: true, value, mask };
172
+ return { __fourState: true, value: BigInt(value), mask: BigInt(mask) };
156
173
  }
157
174
 
158
175
  export function isFourStateValue(v: unknown): v is FourStateValue {
@@ -162,3 +179,37 @@ export function isFourStateValue(v: unknown): v is FourStateValue {
162
179
  (v as FourStateValue).__fourState === true
163
180
  );
164
181
  }
182
+
183
+ // ---------------------------------------------------------------------------
184
+ // Simulation timeout error
185
+ // ---------------------------------------------------------------------------
186
+
187
+ /**
188
+ * Thrown when a simulation helper exceeds its step budget.
189
+ */
190
+ export class SimulationTimeoutError extends Error {
191
+ readonly time: number;
192
+ readonly steps: number;
193
+
194
+ constructor(message: string, time: number, steps: number) {
195
+ super(message);
196
+ this.name = "SimulationTimeoutError";
197
+ this.time = time;
198
+ this.steps = steps;
199
+ }
200
+ }
201
+
202
+ // ---------------------------------------------------------------------------
203
+ // Loop-break types (for Phase 3c)
204
+ // ---------------------------------------------------------------------------
205
+
206
+ /** Specifies a false-loop to ignore during compilation. */
207
+ export interface LoopBreak {
208
+ from: string;
209
+ to: string;
210
+ }
211
+
212
+ /** Specifies a true-loop with a convergence iteration limit. */
213
+ export interface TrueLoopSpec extends LoopBreak {
214
+ maxIter: number;
215
+ }