@celox-sim/celox 0.1.13 → 0.1.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/dist/dut.d.ts +1 -1
- package/dist/dut.d.ts.map +1 -1
- package/dist/dut.js +23 -10
- package/dist/dut.js.map +1 -1
- package/dist/index.d.ts +8 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -10
- package/dist/index.js.map +1 -1
- package/dist/napi-bridge.d.ts +1 -1
- package/dist/napi-bridge.d.ts.map +1 -1
- package/dist/napi-bridge.js +1 -1
- package/dist/napi-bridge.js.map +1 -1
- package/dist/napi-helpers.d.ts +17 -4
- package/dist/napi-helpers.d.ts.map +1 -1
- package/dist/napi-helpers.js +36 -4
- package/dist/napi-helpers.js.map +1 -1
- package/dist/simulation.d.ts +2 -2
- package/dist/simulation.d.ts.map +1 -1
- package/dist/simulation.js +30 -14
- package/dist/simulation.js.map +1 -1
- package/dist/simulator.d.ts +2 -2
- package/dist/simulator.d.ts.map +1 -1
- package/dist/simulator.js +29 -13
- package/dist/simulator.js.map +1 -1
- package/dist/types.d.ts +12 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/src/dut.test.ts +1037 -742
- package/src/dut.ts +580 -528
- package/src/e2e.bench.ts +512 -509
- package/src/e2e.test.ts +2056 -1574
- package/src/index.ts +47 -46
- package/src/matchers.ts +81 -81
- package/src/napi-bridge.ts +5 -5
- package/src/napi-helpers.ts +498 -400
- package/src/simulation.test.ts +395 -368
- package/src/simulation.ts +509 -430
- package/src/simulator.test.ts +201 -168
- package/src/simulator.ts +328 -267
- package/src/types.ts +108 -98
package/src/simulation.test.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import {
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { type NativeCreateSimulationFn, Simulation } from "./simulation.js";
|
|
3
3
|
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
CreateResult,
|
|
5
|
+
ModuleDefinition,
|
|
6
|
+
NativeSimulationHandle,
|
|
7
7
|
} from "./types.js";
|
|
8
8
|
import { SimulationTimeoutError } from "./types.js";
|
|
9
9
|
|
|
@@ -12,78 +12,107 @@ import { SimulationTimeoutError } from "./types.js";
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
|
|
14
14
|
interface TopPorts {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
rst: bigint;
|
|
16
|
+
d: bigint;
|
|
17
|
+
readonly q: bigint;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const TopModule: ModuleDefinition<TopPorts> = {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
__celox_module: true,
|
|
22
|
+
name: "Top",
|
|
23
|
+
sources: [{ path: "", content: "module Top ..." }],
|
|
24
|
+
ports: {
|
|
25
|
+
clk: { direction: "input", type: "clock", width: 1 },
|
|
26
|
+
rst: { direction: "input", type: "reset", width: 1 },
|
|
27
|
+
d: { direction: "input", type: "logic", width: 8 },
|
|
28
|
+
q: { direction: "output", type: "logic", width: 8 },
|
|
29
|
+
},
|
|
30
|
+
events: ["clk"],
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
function createMockNative(opts?: {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
resetTypeKind?: string;
|
|
35
|
+
associatedClock?: string;
|
|
36
36
|
}): {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
create: NativeCreateSimulationFn;
|
|
38
|
+
handle: NativeSimulationHandle;
|
|
39
|
+
buffer: SharedArrayBuffer;
|
|
40
40
|
} {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
41
|
+
const buffer = new SharedArrayBuffer(64);
|
|
42
|
+
let currentTime = 0;
|
|
43
|
+
|
|
44
|
+
const handle: NativeSimulationHandle = {
|
|
45
|
+
addClock: vi.fn(),
|
|
46
|
+
schedule: vi.fn(),
|
|
47
|
+
runUntil: vi.fn().mockImplementation((endTime: number) => {
|
|
48
|
+
// Simulate: q = d after running
|
|
49
|
+
const view = new DataView(buffer);
|
|
50
|
+
view.setUint8(4, view.getUint8(2));
|
|
51
|
+
currentTime = endTime;
|
|
52
|
+
}),
|
|
53
|
+
step: vi.fn().mockImplementation(() => {
|
|
54
|
+
currentTime += 5;
|
|
55
|
+
const view = new DataView(buffer);
|
|
56
|
+
view.setUint8(4, view.getUint8(2));
|
|
57
|
+
// Toggle clock (offset 6) on each step to simulate half-period=5
|
|
58
|
+
view.setUint8(6, view.getUint8(6) === 0 ? 1 : 0);
|
|
59
|
+
return currentTime;
|
|
60
|
+
}),
|
|
61
|
+
time: vi.fn().mockImplementation(() => currentTime),
|
|
62
|
+
nextEventTime: vi.fn().mockReturnValue(null),
|
|
63
|
+
evalComb: vi.fn().mockImplementation(() => {
|
|
64
|
+
const view = new DataView(buffer);
|
|
65
|
+
view.setUint8(4, view.getUint8(2));
|
|
66
|
+
}),
|
|
67
|
+
dump: vi.fn(),
|
|
68
|
+
dispose: vi.fn(),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const resetTypeKind = opts?.resetTypeKind ?? "reset_async_high";
|
|
72
|
+
const associatedClock = opts?.associatedClock;
|
|
73
|
+
|
|
74
|
+
const create: NativeCreateSimulationFn = vi.fn().mockReturnValue({
|
|
75
|
+
buffer,
|
|
76
|
+
layout: {
|
|
77
|
+
clk: {
|
|
78
|
+
offset: 6,
|
|
79
|
+
width: 1,
|
|
80
|
+
byteSize: 1,
|
|
81
|
+
is4state: false,
|
|
82
|
+
direction: "input",
|
|
83
|
+
typeKind: "clock",
|
|
84
|
+
},
|
|
85
|
+
rst: {
|
|
86
|
+
offset: 0,
|
|
87
|
+
width: 1,
|
|
88
|
+
byteSize: 1,
|
|
89
|
+
is4state: false,
|
|
90
|
+
direction: "input",
|
|
91
|
+
typeKind: resetTypeKind,
|
|
92
|
+
...(associatedClock ? { associatedClock } : {}),
|
|
93
|
+
},
|
|
94
|
+
d: {
|
|
95
|
+
offset: 2,
|
|
96
|
+
width: 8,
|
|
97
|
+
byteSize: 1,
|
|
98
|
+
is4state: false,
|
|
99
|
+
direction: "input",
|
|
100
|
+
typeKind: "logic",
|
|
101
|
+
},
|
|
102
|
+
q: {
|
|
103
|
+
offset: 4,
|
|
104
|
+
width: 8,
|
|
105
|
+
byteSize: 1,
|
|
106
|
+
is4state: false,
|
|
107
|
+
direction: "output",
|
|
108
|
+
typeKind: "logic",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
events: { clk: 0 },
|
|
112
|
+
handle,
|
|
113
|
+
} satisfies CreateResult<NativeSimulationHandle>);
|
|
114
|
+
|
|
115
|
+
return { create, handle, buffer };
|
|
87
116
|
}
|
|
88
117
|
|
|
89
118
|
// ---------------------------------------------------------------------------
|
|
@@ -91,303 +120,301 @@ function createMockNative(opts?: {
|
|
|
91
120
|
// ---------------------------------------------------------------------------
|
|
92
121
|
|
|
93
122
|
describe("Simulation", () => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
expect(() => sim.reset("nonexistent")).toThrow("Unknown port");
|
|
392
|
-
});
|
|
123
|
+
test("create and addClock", () => {
|
|
124
|
+
const mock = createMockNative();
|
|
125
|
+
const sim = Simulation.create(TopModule, {
|
|
126
|
+
__nativeCreate: mock.create,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
sim.addClock("clk", { period: 10 });
|
|
130
|
+
expect(mock.handle.addClock).toHaveBeenCalledWith(0, 10, 0);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("addClock with initialDelay", () => {
|
|
134
|
+
const mock = createMockNative();
|
|
135
|
+
const sim = Simulation.create(TopModule, {
|
|
136
|
+
__nativeCreate: mock.create,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
sim.addClock("clk", { period: 10, initialDelay: 5 });
|
|
140
|
+
expect(mock.handle.addClock).toHaveBeenCalledWith(0, 10, 5);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("schedule", () => {
|
|
144
|
+
const mock = createMockNative();
|
|
145
|
+
const sim = Simulation.create(TopModule, {
|
|
146
|
+
__nativeCreate: mock.create,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
sim.schedule("clk", { time: 50, value: 1 });
|
|
150
|
+
expect(mock.handle.schedule).toHaveBeenCalledWith(0, 50, 1);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("runUntil", () => {
|
|
154
|
+
const mock = createMockNative();
|
|
155
|
+
const sim = Simulation.create(TopModule, {
|
|
156
|
+
__nativeCreate: mock.create,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
sim.dut.d = 42n;
|
|
160
|
+
sim.runUntil(100);
|
|
161
|
+
|
|
162
|
+
expect(mock.handle.runUntil).toHaveBeenCalledWith(100);
|
|
163
|
+
expect(sim.dut.q).toBe(42n);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("step", () => {
|
|
167
|
+
const mock = createMockNative();
|
|
168
|
+
const sim = Simulation.create(TopModule, {
|
|
169
|
+
__nativeCreate: mock.create,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
sim.dut.d = 0xabn;
|
|
173
|
+
const t = sim.step();
|
|
174
|
+
|
|
175
|
+
expect(t).toBe(5);
|
|
176
|
+
expect(sim.dut.q).toBe(0xabn);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("time", () => {
|
|
180
|
+
const mock = createMockNative();
|
|
181
|
+
const sim = Simulation.create(TopModule, {
|
|
182
|
+
__nativeCreate: mock.create,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(sim.time()).toBe(0);
|
|
186
|
+
sim.runUntil(100);
|
|
187
|
+
expect(sim.time()).toBe(100);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("unknown event throws", () => {
|
|
191
|
+
const mock = createMockNative();
|
|
192
|
+
const sim = Simulation.create(TopModule, {
|
|
193
|
+
__nativeCreate: mock.create,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
expect(() => sim.addClock("bad", { period: 10 })).toThrow("Unknown event");
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("dispose prevents further operations", () => {
|
|
200
|
+
const mock = createMockNative();
|
|
201
|
+
const sim = Simulation.create(TopModule, {
|
|
202
|
+
__nativeCreate: mock.create,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
sim.dispose();
|
|
206
|
+
expect(() => sim.runUntil(100)).toThrow("disposed");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("dump delegates to handle", () => {
|
|
210
|
+
const mock = createMockNative();
|
|
211
|
+
const sim = Simulation.create(TopModule, {
|
|
212
|
+
__nativeCreate: mock.create,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
sim.dump(99);
|
|
216
|
+
expect(mock.handle.dump).toHaveBeenCalledWith(99);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("create throws without native binding", () => {
|
|
220
|
+
expect(() => {
|
|
221
|
+
Simulation.create(TopModule);
|
|
222
|
+
}).toThrow("Native simulator binding not loaded");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("runUntil with maxSteps: fast path when omitted", () => {
|
|
226
|
+
const mock = createMockNative();
|
|
227
|
+
const sim = Simulation.create(TopModule, {
|
|
228
|
+
__nativeCreate: mock.create,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
sim.runUntil(100);
|
|
232
|
+
// Without maxSteps, the Rust fast-path should be used
|
|
233
|
+
expect(mock.handle.runUntil).toHaveBeenCalledWith(100);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("runUntil with maxSteps: throws SimulationTimeoutError", () => {
|
|
237
|
+
const mock = createMockNative();
|
|
238
|
+
const sim = Simulation.create(TopModule, {
|
|
239
|
+
__nativeCreate: mock.create,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// step mock increments time by 5 each call, so reaching 10000 requires 2000 steps
|
|
243
|
+
// With maxSteps=10 we'll exhaust before getting there
|
|
244
|
+
expect(() => sim.runUntil(10000, { maxSteps: 10 })).toThrow(
|
|
245
|
+
SimulationTimeoutError,
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("runUntil with maxSteps: timeout error has correct properties", () => {
|
|
250
|
+
const mock = createMockNative();
|
|
251
|
+
const sim = Simulation.create(TopModule, {
|
|
252
|
+
__nativeCreate: mock.create,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
sim.runUntil(10000, { maxSteps: 5 });
|
|
257
|
+
expect.unreachable();
|
|
258
|
+
} catch (e) {
|
|
259
|
+
expect(e).toBeInstanceOf(SimulationTimeoutError);
|
|
260
|
+
const err = e as SimulationTimeoutError;
|
|
261
|
+
expect(err.steps).toBe(5);
|
|
262
|
+
expect(err.time).toBeGreaterThan(0);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("waitUntil: returns time when condition is met", () => {
|
|
267
|
+
const mock = createMockNative();
|
|
268
|
+
const sim = Simulation.create(TopModule, {
|
|
269
|
+
__nativeCreate: mock.create,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
sim.dut.d = 42n;
|
|
273
|
+
let callCount = 0;
|
|
274
|
+
const t = sim.waitUntil(() => {
|
|
275
|
+
callCount++;
|
|
276
|
+
return callCount >= 3;
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
expect(t).toBeGreaterThan(0);
|
|
280
|
+
expect(callCount).toBe(3);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("waitUntil: throws on timeout", () => {
|
|
284
|
+
const mock = createMockNative();
|
|
285
|
+
const sim = Simulation.create(TopModule, {
|
|
286
|
+
__nativeCreate: mock.create,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
expect(() => sim.waitUntil(() => false, { maxSteps: 5 })).toThrow(
|
|
290
|
+
SimulationTimeoutError,
|
|
291
|
+
);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("waitForCycles: counts rising edges", () => {
|
|
295
|
+
const mock = createMockNative();
|
|
296
|
+
const sim = Simulation.create(TopModule, {
|
|
297
|
+
__nativeCreate: mock.create,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
sim.addClock("clk", { period: 10 });
|
|
301
|
+
const t = sim.waitForCycles("clk", 3);
|
|
302
|
+
// clk toggles each step: 0→1→0→1→0→1 (5 steps for 3 rising edges)
|
|
303
|
+
expect(mock.handle.step).toHaveBeenCalledTimes(5);
|
|
304
|
+
expect(t).toBe(25);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("waitForCycles: throws without addClock", () => {
|
|
308
|
+
const mock = createMockNative();
|
|
309
|
+
const sim = Simulation.create(TopModule, {
|
|
310
|
+
__nativeCreate: mock.create,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
expect(() => sim.waitForCycles("clk", 3)).toThrow("No clock registered");
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test("reset: active-high with associatedClock steps until target time", () => {
|
|
317
|
+
const mock = createMockNative({ associatedClock: "clk" });
|
|
318
|
+
const sim = Simulation.create(TopModule, {
|
|
319
|
+
__nativeCreate: mock.create,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
sim.addClock("clk", { period: 10 });
|
|
323
|
+
sim.reset("rst");
|
|
324
|
+
// Default activeCycles=2: 3 steps for 2 rising edges (0→1→0→1)
|
|
325
|
+
expect(mock.handle.step).toHaveBeenCalledTimes(3);
|
|
326
|
+
// Released to inactive value (0 for active-high)
|
|
327
|
+
const view = new DataView(mock.buffer);
|
|
328
|
+
expect(view.getUint8(0)).toBe(0);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("reset: custom activeCycles with associatedClock", () => {
|
|
332
|
+
const mock = createMockNative({ associatedClock: "clk" });
|
|
333
|
+
const sim = Simulation.create(TopModule, {
|
|
334
|
+
__nativeCreate: mock.create,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
sim.addClock("clk", { period: 10 });
|
|
338
|
+
sim.reset("rst", { activeCycles: 3 });
|
|
339
|
+
// 3 cycles: 5 steps for 3 rising edges (0→1→0→1→0→1)
|
|
340
|
+
expect(mock.handle.step).toHaveBeenCalledTimes(5);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test("reset: explicit duration overrides cycle calculation", () => {
|
|
344
|
+
const mock = createMockNative({ associatedClock: "clk" });
|
|
345
|
+
const sim = Simulation.create(TopModule, {
|
|
346
|
+
__nativeCreate: mock.create,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
sim.addClock("clk", { period: 10 });
|
|
350
|
+
sim.reset("rst", { duration: 50 });
|
|
351
|
+
// Explicit duration → runUntil(0 + 50 = 50)
|
|
352
|
+
expect(mock.handle.runUntil).toHaveBeenCalledWith(50);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test("reset: active-low with associatedClock asserts 0 then releases to 1", () => {
|
|
356
|
+
const mock = createMockNative({
|
|
357
|
+
resetTypeKind: "reset_async_low",
|
|
358
|
+
associatedClock: "clk",
|
|
359
|
+
});
|
|
360
|
+
const sim = Simulation.create(TopModule, {
|
|
361
|
+
__nativeCreate: mock.create,
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
sim.addClock("clk", { period: 10 });
|
|
365
|
+
sim.reset("rst");
|
|
366
|
+
// active-low: releases to 1
|
|
367
|
+
const view = new DataView(mock.buffer);
|
|
368
|
+
expect(view.getUint8(0)).toBe(1);
|
|
369
|
+
// activeCycles=2: 3 steps for 2 rising edges
|
|
370
|
+
expect(mock.handle.step).toHaveBeenCalledTimes(3);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
test("reset: throws when no associatedClock and no duration", () => {
|
|
374
|
+
// No associatedClock in layout
|
|
375
|
+
const mock = createMockNative();
|
|
376
|
+
const sim = Simulation.create(TopModule, {
|
|
377
|
+
__nativeCreate: mock.create,
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
expect(() => sim.reset("rst")).toThrow("has no associated clock");
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test("reset: no associatedClock but duration specified works", () => {
|
|
384
|
+
// No associatedClock in layout, but duration is given
|
|
385
|
+
const mock = createMockNative();
|
|
386
|
+
const sim = Simulation.create(TopModule, {
|
|
387
|
+
__nativeCreate: mock.create,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
sim.reset("rst", { duration: 100 });
|
|
391
|
+
expect(mock.handle.runUntil).toHaveBeenCalledWith(100);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
test("reset: throws when associatedClock not registered via addClock", () => {
|
|
395
|
+
const mock = createMockNative({ associatedClock: "clk" });
|
|
396
|
+
const sim = Simulation.create(TopModule, {
|
|
397
|
+
__nativeCreate: mock.create,
|
|
398
|
+
});
|
|
399
|
+
// addClock not called
|
|
400
|
+
expect(() => sim.reset("rst")).toThrow("No clock registered for 'clk'");
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test("reset: throws on non-reset port", () => {
|
|
404
|
+
const mock = createMockNative();
|
|
405
|
+
const sim = Simulation.create(TopModule, {
|
|
406
|
+
__nativeCreate: mock.create,
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
expect(() => sim.reset("d")).toThrow("not a reset signal");
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
test("reset: throws on unknown port", () => {
|
|
413
|
+
const mock = createMockNative();
|
|
414
|
+
const sim = Simulation.create(TopModule, {
|
|
415
|
+
__nativeCreate: mock.create,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
expect(() => sim.reset("nonexistent")).toThrow("Unknown port");
|
|
419
|
+
});
|
|
393
420
|
});
|