@celox-sim/celox 0.1.12 → 0.1.14

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.bench.ts CHANGED
@@ -9,14 +9,23 @@
9
9
  * 4. Simulator::tick vs Simulation::step overhead
10
10
  */
11
11
 
12
- import { bench, describe, afterAll } from "vitest";
13
- import { Simulator } from "./simulator.js";
12
+ import { readFileSync } from "node:fs";
13
+ import { dirname, resolve } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ import { afterAll, bench, describe } from "vitest";
16
+ import { createSimulatorBridge, loadNativeAddon } from "./napi-helpers.js";
14
17
  import { Simulation } from "./simulation.js";
15
- import type { ModuleDefinition, SimulationTimeoutError } from "./types.js";
16
- import {
17
- loadNativeAddon,
18
- createSimulatorBridge,
19
- } from "./napi-helpers.js";
18
+ import { Simulator } from "./simulator.js";
19
+ import type { ModuleDefinition } from "./types.js";
20
+
21
+ const __benchDir = dirname(fileURLToPath(import.meta.url));
22
+ const VERYL_STD = resolve(
23
+ __benchDir,
24
+ "../../../deps/veryl/crates/std/veryl/src",
25
+ );
26
+ function readVeryl(...parts: string[]): string {
27
+ return readFileSync(resolve(VERYL_STD, ...parts), "utf8");
28
+ }
20
29
 
21
30
  const CODE = `
22
31
  module Top #(
@@ -39,111 +48,111 @@ const CODE = `
39
48
  `;
40
49
 
41
50
  interface TopPorts {
42
- rst: bigint;
43
- readonly cnt: { at(i: number): bigint; readonly length: number };
51
+ rst: bigint;
52
+ readonly cnt: { at(i: number): bigint; readonly length: number };
44
53
  }
45
54
 
46
55
  describe("simulation", () => {
47
- bench(
48
- "simulation_build_top_n1000",
49
- () => {
50
- const sim = Simulator.fromSource<TopPorts>(CODE, "Top");
51
- sim.dispose();
52
- },
53
- { iterations: 3, time: 0 },
54
- );
55
-
56
- const sim = Simulator.fromSource<TopPorts>(CODE, "Top");
57
-
58
- // Reset sequence
59
- sim.dut.rst = 1n;
60
- sim.tick();
61
- sim.dut.rst = 0n;
62
- sim.tick();
63
-
64
- afterAll(() => {
65
- sim.dispose();
66
- });
67
-
68
- bench("simulation_tick_top_n1000_x1", () => {
69
- sim.tick();
70
- });
71
-
72
- bench(
73
- "simulation_tick_top_n1000_x1000000",
74
- () => {
75
- for (let i = 0; i < 1_000_000; i++) {
76
- sim.tick();
77
- }
78
- },
79
- { iterations: 3, time: 0 },
80
- );
81
-
82
- // Testbench pattern: write input + tick + read back
83
- bench("testbench_tick_top_n1000_x1", () => {
84
- sim.dut.rst = 0n;
85
- sim.tick();
86
- // biome-ignore lint: read to measure full testbench cycle
87
- sim.dut.rst;
88
- });
89
-
90
- bench(
91
- "testbench_tick_top_n1000_x1000000",
92
- () => {
93
- for (let i = 0; i < 1_000_000; i++) {
94
- sim.dut.rst = 0n;
95
- sim.tick();
96
- // biome-ignore lint: read to measure full testbench cycle
97
- sim.dut.rst;
98
- }
99
- },
100
- { iterations: 3, time: 0 },
101
- );
102
-
103
- // Array access via .at() — use ModuleDefinition with arrayDims
104
- const addon = loadNativeAddon();
105
- const TopModule: ModuleDefinition<TopPorts> = {
106
- __celox_module: true,
107
- name: "Top",
108
- source: CODE,
109
- ports: {
110
- clk: { direction: "input", type: "clock", width: 1 },
111
- rst: { direction: "input", type: "reset", width: 1 },
112
- cnt: { direction: "output", type: "logic", width: 32, arrayDims: [1000] },
113
- },
114
- events: ["clk"],
115
- };
116
- const simArr = Simulator.create<TopPorts>(TopModule, {
117
- __nativeCreate: createSimulatorBridge(addon),
118
- });
119
- simArr.dut.rst = 1n;
120
- simArr.tick();
121
- simArr.dut.rst = 0n;
122
- simArr.tick();
123
-
124
- afterAll(() => {
125
- simArr.dispose();
126
- });
127
-
128
- bench("testbench_array_tick_top_n1000_x1", () => {
129
- simArr.dut.rst = 0n;
130
- simArr.tick();
131
- // biome-ignore lint: read array element to measure .at() overhead
132
- simArr.dut.cnt.at(0);
133
- });
134
-
135
- bench(
136
- "testbench_array_tick_top_n1000_x1000000",
137
- () => {
138
- for (let i = 0; i < 1_000_000; i++) {
139
- simArr.dut.rst = 0n;
140
- simArr.tick();
141
- // biome-ignore lint: read array element to measure .at() overhead
142
- simArr.dut.cnt.at(0);
143
- }
144
- },
145
- { iterations: 3, time: 0 },
146
- );
56
+ bench(
57
+ "simulation_build_top_n1000",
58
+ () => {
59
+ const sim = Simulator.fromSource<TopPorts>(CODE, "Top");
60
+ sim.dispose();
61
+ },
62
+ { iterations: 3, time: 0 },
63
+ );
64
+
65
+ const sim = Simulator.fromSource<TopPorts>(CODE, "Top");
66
+
67
+ // Reset sequence
68
+ sim.dut.rst = 1n;
69
+ sim.tick();
70
+ sim.dut.rst = 0n;
71
+ sim.tick();
72
+
73
+ afterAll(() => {
74
+ sim.dispose();
75
+ });
76
+
77
+ bench("simulation_tick_top_n1000_x1", () => {
78
+ sim.tick();
79
+ });
80
+
81
+ bench(
82
+ "simulation_tick_top_n1000_x1000000",
83
+ () => {
84
+ for (let i = 0; i < 1_000_000; i++) {
85
+ sim.tick();
86
+ }
87
+ },
88
+ { iterations: 3, time: 0 },
89
+ );
90
+
91
+ // Testbench pattern: write input + tick + read back
92
+ bench("testbench_tick_top_n1000_x1", () => {
93
+ sim.dut.rst = 0n;
94
+ sim.tick();
95
+ //read to measure full testbench cycle
96
+ sim.dut.rst;
97
+ });
98
+
99
+ bench(
100
+ "testbench_tick_top_n1000_x1000000",
101
+ () => {
102
+ for (let i = 0; i < 1_000_000; i++) {
103
+ sim.dut.rst = 0n;
104
+ sim.tick();
105
+ //read to measure full testbench cycle
106
+ sim.dut.rst;
107
+ }
108
+ },
109
+ { iterations: 3, time: 0 },
110
+ );
111
+
112
+ // Array access via .at() — use ModuleDefinition with arrayDims
113
+ const addon = loadNativeAddon();
114
+ const TopModule: ModuleDefinition<TopPorts> = {
115
+ __celox_module: true,
116
+ name: "Top",
117
+ source: CODE,
118
+ ports: {
119
+ clk: { direction: "input", type: "clock", width: 1 },
120
+ rst: { direction: "input", type: "reset", width: 1 },
121
+ cnt: { direction: "output", type: "logic", width: 32, arrayDims: [1000] },
122
+ },
123
+ events: ["clk"],
124
+ };
125
+ const simArr = Simulator.create<TopPorts>(TopModule, {
126
+ __nativeCreate: createSimulatorBridge(addon),
127
+ });
128
+ simArr.dut.rst = 1n;
129
+ simArr.tick();
130
+ simArr.dut.rst = 0n;
131
+ simArr.tick();
132
+
133
+ afterAll(() => {
134
+ simArr.dispose();
135
+ });
136
+
137
+ bench("testbench_array_tick_top_n1000_x1", () => {
138
+ simArr.dut.rst = 0n;
139
+ simArr.tick();
140
+ //read array element to measure .at() overhead
141
+ simArr.dut.cnt.at(0);
142
+ });
143
+
144
+ bench(
145
+ "testbench_array_tick_top_n1000_x1000000",
146
+ () => {
147
+ for (let i = 0; i < 1_000_000; i++) {
148
+ simArr.dut.rst = 0n;
149
+ simArr.tick();
150
+ //read array element to measure .at() overhead
151
+ simArr.dut.cnt.at(0);
152
+ }
153
+ },
154
+ { iterations: 3, time: 0 },
155
+ );
147
156
  });
148
157
 
149
158
  /**
@@ -153,45 +162,45 @@ describe("simulation", () => {
153
162
  * scheduling overhead of the time-based API.
154
163
  */
155
164
  describe("overhead", () => {
156
- // Simulator.tick — same as Rust simulator_tick_x10000
157
- const simTick = Simulator.fromSource<TopPorts>(CODE, "Top");
158
- simTick.dut.rst = 1n;
159
- simTick.tick();
160
- simTick.dut.rst = 0n;
161
- simTick.tick();
162
-
163
- afterAll(() => {
164
- simTick.dispose();
165
- });
166
-
167
- bench(
168
- "simulator_tick_x10000",
169
- () => {
170
- for (let i = 0; i < 10_000; i++) {
171
- simTick.tick();
172
- }
173
- },
174
- { iterations: 3, time: 0 },
175
- );
176
-
177
- // Simulation.step — same as Rust simulation_step_x20000
178
- const simStep = Simulation.fromSource<TopPorts>(CODE, "Top");
179
- simStep.addClock("clk", { period: 10 });
180
-
181
- afterAll(() => {
182
- simStep.dispose();
183
- });
184
-
185
- bench(
186
- "simulation_step_x20000",
187
- () => {
188
- // 20000 steps = 10000 cycles (rising + falling)
189
- for (let i = 0; i < 20_000; i++) {
190
- simStep.step();
191
- }
192
- },
193
- { iterations: 3, time: 0 },
194
- );
165
+ // Simulator.tick — same as Rust simulator_tick_x10000
166
+ const simTick = Simulator.fromSource<TopPorts>(CODE, "Top");
167
+ simTick.dut.rst = 1n;
168
+ simTick.tick();
169
+ simTick.dut.rst = 0n;
170
+ simTick.tick();
171
+
172
+ afterAll(() => {
173
+ simTick.dispose();
174
+ });
175
+
176
+ bench(
177
+ "simulator_tick_x10000",
178
+ () => {
179
+ for (let i = 0; i < 10_000; i++) {
180
+ simTick.tick();
181
+ }
182
+ },
183
+ { iterations: 3, time: 0 },
184
+ );
185
+
186
+ // Simulation.step — same as Rust simulation_step_x20000
187
+ const simStep = Simulation.fromSource<TopPorts>(CODE, "Top");
188
+ simStep.addClock("clk", { period: 10 });
189
+
190
+ afterAll(() => {
191
+ simStep.dispose();
192
+ });
193
+
194
+ bench(
195
+ "simulation_step_x20000",
196
+ () => {
197
+ // 20000 steps = 10000 cycles (rising + falling)
198
+ for (let i = 0; i < 20_000; i++) {
199
+ simStep.step();
200
+ }
201
+ },
202
+ { iterations: 3, time: 0 },
203
+ );
195
204
  });
196
205
 
197
206
  /**
@@ -199,44 +208,44 @@ describe("overhead", () => {
199
208
  * above but uses the Simulation API instead of Simulator.
200
209
  */
201
210
  describe("simulation-time-based", () => {
202
- bench(
203
- "simulation_time_build_top_n1000",
204
- () => {
205
- const sim = Simulation.fromSource<TopPorts>(CODE, "Top");
206
- sim.dispose();
207
- },
208
- { iterations: 3, time: 0 },
209
- );
210
-
211
- const sim = Simulation.fromSource<TopPorts>(CODE, "Top");
212
- sim.addClock("clk", { period: 10 });
213
-
214
- afterAll(() => {
215
- sim.dispose();
216
- });
217
-
218
- bench("simulation_time_step_x1", () => {
219
- sim.step();
220
- });
221
-
222
- bench(
223
- "simulation_time_step_x1000000",
224
- () => {
225
- for (let i = 0; i < 1_000_000; i++) {
226
- sim.step();
227
- }
228
- },
229
- { iterations: 3, time: 0 },
230
- );
231
-
232
- bench(
233
- "simulation_time_runUntil_1000000",
234
- () => {
235
- const base = sim.time();
236
- sim.runUntil(base + 1_000_000);
237
- },
238
- { iterations: 3, time: 0 },
239
- );
211
+ bench(
212
+ "simulation_time_build_top_n1000",
213
+ () => {
214
+ const sim = Simulation.fromSource<TopPorts>(CODE, "Top");
215
+ sim.dispose();
216
+ },
217
+ { iterations: 3, time: 0 },
218
+ );
219
+
220
+ const sim = Simulation.fromSource<TopPorts>(CODE, "Top");
221
+ sim.addClock("clk", { period: 10 });
222
+
223
+ afterAll(() => {
224
+ sim.dispose();
225
+ });
226
+
227
+ bench("simulation_time_step_x1", () => {
228
+ sim.step();
229
+ });
230
+
231
+ bench(
232
+ "simulation_time_step_x1000000",
233
+ () => {
234
+ for (let i = 0; i < 1_000_000; i++) {
235
+ sim.step();
236
+ }
237
+ },
238
+ { iterations: 3, time: 0 },
239
+ );
240
+
241
+ bench(
242
+ "simulation_time_runUntil_1000000",
243
+ () => {
244
+ const base = sim.time();
245
+ sim.runUntil(base + 1_000_000);
246
+ },
247
+ { iterations: 3, time: 0 },
248
+ );
240
249
  });
241
250
 
242
251
  /**
@@ -246,7 +255,7 @@ describe("simulation-time-based", () => {
246
255
  * maxSteps guard to measure overhead.
247
256
  */
248
257
  describe("testbench-helpers", () => {
249
- const COUNTER_CODE = `
258
+ const COUNTER_CODE = `
250
259
  module Counter (
251
260
  clk: input clock,
252
261
  rst: input reset,
@@ -269,71 +278,71 @@ describe("testbench-helpers", () => {
269
278
  }
270
279
  `;
271
280
 
272
- interface CounterPorts {
273
- rst: bigint;
274
- en: bigint;
275
- readonly count: bigint;
276
- }
277
-
278
- // waitForCycles benchmark
279
- const simWait = Simulation.fromSource<CounterPorts>(COUNTER_CODE, "Counter");
280
- simWait.addClock("clk", { period: 10 });
281
- simWait.dut.rst = 1n;
282
- simWait.runUntil(20);
283
- simWait.dut.rst = 0n;
284
- simWait.dut.en = 1n;
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 = 1n;
312
- simRun.runUntil(20);
313
- simRun.dut.rst = 0n;
314
- simRun.dut.en = 1n;
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
- );
281
+ interface CounterPorts {
282
+ rst: bigint;
283
+ en: bigint;
284
+ readonly count: bigint;
285
+ }
286
+
287
+ // waitForCycles benchmark
288
+ const simWait = Simulation.fromSource<CounterPorts>(COUNTER_CODE, "Counter");
289
+ simWait.addClock("clk", { period: 10 });
290
+ simWait.dut.rst = 1n;
291
+ simWait.runUntil(20);
292
+ simWait.dut.rst = 0n;
293
+ simWait.dut.en = 1n;
294
+
295
+ afterAll(() => {
296
+ simWait.dispose();
297
+ });
298
+
299
+ bench(
300
+ "waitForCycles_x1000",
301
+ () => {
302
+ simWait.waitForCycles("clk", 1000);
303
+ },
304
+ { iterations: 3, time: 0 },
305
+ );
306
+
307
+ bench(
308
+ "manual_step_loop_x2000",
309
+ () => {
310
+ for (let i = 0; i < 2000; i++) {
311
+ simWait.step();
312
+ }
313
+ },
314
+ { iterations: 3, time: 0 },
315
+ );
316
+
317
+ // runUntil: fast Rust path vs guarded TS path
318
+ const simRun = Simulation.fromSource<CounterPorts>(COUNTER_CODE, "Counter");
319
+ simRun.addClock("clk", { period: 10 });
320
+ simRun.dut.rst = 1n;
321
+ simRun.runUntil(20);
322
+ simRun.dut.rst = 0n;
323
+ simRun.dut.en = 1n;
324
+
325
+ afterAll(() => {
326
+ simRun.dispose();
327
+ });
328
+
329
+ bench(
330
+ "runUntil_fast_path_100000",
331
+ () => {
332
+ const base = simRun.time();
333
+ simRun.runUntil(base + 100_000);
334
+ },
335
+ { iterations: 3, time: 0 },
336
+ );
337
+
338
+ bench(
339
+ "runUntil_guarded_100000",
340
+ () => {
341
+ const base = simRun.time();
342
+ simRun.runUntil(base + 100_000, { maxSteps: 1_000_000 });
343
+ },
344
+ { iterations: 3, time: 0 },
345
+ );
337
346
  });
338
347
 
339
348
  /**
@@ -342,62 +351,370 @@ describe("testbench-helpers", () => {
342
351
  * Compares build time and tick performance with and without optimization.
343
352
  */
344
353
  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 = 1n;
367
- simNoOpt.tick();
368
- simNoOpt.dut.rst = 0n;
369
- simNoOpt.tick();
370
-
371
- const simOpt = Simulator.fromSource<TopPorts>(CODE, "Top", {
372
- optimize: true,
373
- });
374
- simOpt.dut.rst = 1n;
375
- simOpt.tick();
376
- simOpt.dut.rst = 0n;
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
- );
354
+ bench(
355
+ "build_without_optimize",
356
+ () => {
357
+ const sim = Simulator.fromSource<TopPorts>(CODE, "Top");
358
+ sim.dispose();
359
+ },
360
+ { iterations: 3, time: 0 },
361
+ );
362
+
363
+ bench(
364
+ "build_with_optimize",
365
+ () => {
366
+ const sim = Simulator.fromSource<TopPorts>(CODE, "Top", {
367
+ optimize: true,
368
+ });
369
+ sim.dispose();
370
+ },
371
+ { iterations: 3, time: 0 },
372
+ );
373
+
374
+ const simNoOpt = Simulator.fromSource<TopPorts>(CODE, "Top");
375
+ simNoOpt.dut.rst = 1n;
376
+ simNoOpt.tick();
377
+ simNoOpt.dut.rst = 0n;
378
+ simNoOpt.tick();
379
+
380
+ const simOpt = Simulator.fromSource<TopPorts>(CODE, "Top", {
381
+ optimize: true,
382
+ });
383
+ simOpt.dut.rst = 1n;
384
+ simOpt.tick();
385
+ simOpt.dut.rst = 0n;
386
+ simOpt.tick();
387
+
388
+ afterAll(() => {
389
+ simNoOpt.dispose();
390
+ simOpt.dispose();
391
+ });
392
+
393
+ bench(
394
+ "tick_x10000_without_optimize",
395
+ () => {
396
+ for (let i = 0; i < 10_000; i++) {
397
+ simNoOpt.tick();
398
+ }
399
+ },
400
+ { iterations: 3, time: 0 },
401
+ );
402
+
403
+ bench(
404
+ "tick_x10000_with_optimize",
405
+ () => {
406
+ for (let i = 0; i < 10_000; i++) {
407
+ simOpt.tick();
408
+ }
409
+ },
410
+ { iterations: 3, time: 0 },
411
+ );
412
+ });
413
+
414
+ // ────────────────────────────────────────────────────────────
415
+ // Stdlib benchmarks — mirrors crates/celox/benches/simulation.rs
416
+ // ────────────────────────────────────────────────────────────
417
+
418
+ // --- Linear SEC (P=6): Hamming encoder/decoder, combinational ---
419
+
420
+ const LINEAR_SEC_SRC =
421
+ readVeryl("coding/linear_sec_encoder.veryl") +
422
+ readVeryl("coding/linear_sec_decoder.veryl") +
423
+ `
424
+ module Top #(
425
+ param P: u32 = 6,
426
+ const K: u32 = (1 << P) - 1,
427
+ const N: u32 = K - P,
428
+ )(
429
+ i_word : input logic<N>,
430
+ o_codeword : output logic<K>,
431
+ o_word : output logic<N>,
432
+ o_corrected: output logic,
433
+ ) {
434
+ inst u_enc: linear_sec_encoder #(P: P) (i_word, o_codeword);
435
+ inst u_dec: linear_sec_decoder #(P: P) (
436
+ i_codeword: o_codeword,
437
+ o_word,
438
+ o_corrected,
439
+ );
440
+ }
441
+ `;
442
+
443
+ interface LinearSecPorts {
444
+ i_word: bigint;
445
+ readonly o_codeword: bigint;
446
+ readonly o_word: bigint;
447
+ readonly o_corrected: bigint;
448
+ }
449
+
450
+ describe("stdlib-linear-sec", () => {
451
+ bench(
452
+ "simulation_build_linear_sec_p6",
453
+ () => {
454
+ const sim = Simulator.fromSource<LinearSecPorts>(LINEAR_SEC_SRC, "Top");
455
+ sim.dispose();
456
+ },
457
+ { iterations: 3, time: 0 },
458
+ );
459
+
460
+ const sim = Simulator.fromSource<LinearSecPorts>(LINEAR_SEC_SRC, "Top");
461
+
462
+ afterAll(() => {
463
+ sim.dispose();
464
+ });
465
+
466
+ let linearSecInput = 0n;
467
+ bench("simulation_eval_linear_sec_p6_x1", () => {
468
+ sim.dut.i_word = linearSecInput++;
469
+ //read to measure eval
470
+ sim.dut.o_word;
471
+ });
472
+
473
+ bench(
474
+ "simulation_eval_linear_sec_p6_x1000000",
475
+ () => {
476
+ let input = 0n;
477
+ for (let i = 0; i < 1_000_000; i++) {
478
+ sim.dut.i_word = input++;
479
+ //read to measure eval
480
+ sim.dut.o_word;
481
+ }
482
+ },
483
+ { iterations: 3, time: 0 },
484
+ );
485
+
486
+ bench(
487
+ "testbench_eval_linear_sec_p6_x1000000",
488
+ () => {
489
+ let input = 0n;
490
+ for (let i = 0; i < 1_000_000; i++) {
491
+ sim.dut.i_word = input++;
492
+ //read corrected flag
493
+ sim.dut.o_corrected;
494
+ }
495
+ },
496
+ { iterations: 3, time: 0 },
497
+ );
498
+ });
499
+
500
+ // --- Countones (W=64): recursive combinational popcount tree ---
501
+
502
+ const COUNTONES_SRC =
503
+ readVeryl("countones/countones.veryl") +
504
+ `
505
+ module Top (
506
+ i_data: input logic<64>,
507
+ o_ones: output logic<7>,
508
+ ) {
509
+ inst u: countones #(W: 64) (i_data, o_ones);
510
+ }
511
+ `;
512
+
513
+ interface CountonesPorts {
514
+ i_data: bigint;
515
+ readonly o_ones: bigint;
516
+ }
517
+
518
+ describe("stdlib-countones", () => {
519
+ bench(
520
+ "simulation_build_countones_w64",
521
+ () => {
522
+ const sim = Simulator.fromSource<CountonesPorts>(COUNTONES_SRC, "Top");
523
+ sim.dispose();
524
+ },
525
+ { iterations: 3, time: 0 },
526
+ );
527
+
528
+ const sim = Simulator.fromSource<CountonesPorts>(COUNTONES_SRC, "Top");
529
+
530
+ afterAll(() => {
531
+ sim.dispose();
532
+ });
533
+
534
+ let countonesInput = 0n;
535
+ bench("simulation_eval_countones_w64_x1", () => {
536
+ sim.dut.i_data = countonesInput++;
537
+ //read to measure eval
538
+ sim.dut.o_ones;
539
+ });
540
+
541
+ bench(
542
+ "simulation_eval_countones_w64_x1000000",
543
+ () => {
544
+ let input = 0n;
545
+ for (let i = 0; i < 1_000_000; i++) {
546
+ sim.dut.i_data = input++;
547
+ //read to measure eval
548
+ sim.dut.o_ones;
549
+ }
550
+ },
551
+ { iterations: 3, time: 0 },
552
+ );
553
+ });
554
+
555
+ // --- std::counter (WIDTH=32): sequential up-counter ---
556
+
557
+ const STD_COUNTER_SRC =
558
+ readVeryl("counter/counter.veryl") +
559
+ `
560
+ module Top (
561
+ clk : input clock,
562
+ rst : input reset,
563
+ i_up : input logic,
564
+ o_count: output logic<32>,
565
+ ) {
566
+ inst u: counter #(WIDTH: 32) (
567
+ i_clk : clk,
568
+ i_rst : rst,
569
+ i_clear : 1'b0,
570
+ i_set : 1'b0,
571
+ i_set_value : 32'b0,
572
+ i_up,
573
+ i_down : 1'b0,
574
+ o_count,
575
+ o_count_next: _,
576
+ o_wrap_around: _,
577
+ );
578
+ }
579
+ `;
580
+
581
+ interface StdCounterPorts {
582
+ rst: bigint;
583
+ i_up: bigint;
584
+ readonly o_count: bigint;
585
+ }
586
+
587
+ describe("stdlib-counter", () => {
588
+ bench(
589
+ "simulation_build_std_counter_w32",
590
+ () => {
591
+ const sim = Simulator.fromSource<StdCounterPorts>(STD_COUNTER_SRC, "Top");
592
+ sim.dispose();
593
+ },
594
+ { iterations: 3, time: 0 },
595
+ );
596
+
597
+ const sim = Simulator.fromSource<StdCounterPorts>(STD_COUNTER_SRC, "Top");
598
+ sim.dut.rst = 1n;
599
+ sim.dut.i_up = 0n;
600
+ sim.tick();
601
+ sim.dut.rst = 0n;
602
+ sim.dut.i_up = 1n;
603
+ sim.tick();
604
+
605
+ afterAll(() => {
606
+ sim.dispose();
607
+ });
608
+
609
+ bench("simulation_tick_std_counter_w32_x1", () => {
610
+ sim.tick();
611
+ });
612
+
613
+ bench(
614
+ "simulation_tick_std_counter_w32_x1000000",
615
+ () => {
616
+ for (let i = 0; i < 1_000_000; i++) {
617
+ sim.tick();
618
+ }
619
+ },
620
+ { iterations: 3, time: 0 },
621
+ );
622
+
623
+ bench(
624
+ "testbench_tick_std_counter_w32_x1000000",
625
+ () => {
626
+ for (let i = 0; i < 1_000_000; i++) {
627
+ sim.tick();
628
+ //read to measure testbench cycle
629
+ sim.dut.o_count;
630
+ }
631
+ },
632
+ { iterations: 3, time: 0 },
633
+ );
634
+ });
635
+
636
+ // --- std::gray_counter (WIDTH=32): Gray-encoded sequential counter ---
637
+
638
+ const GRAY_COUNTER_SRC =
639
+ readVeryl("counter/counter.veryl") +
640
+ readVeryl("gray/gray_encoder.veryl") +
641
+ readVeryl("gray/gray_counter.veryl") +
642
+ `
643
+ module Top (
644
+ clk : input clock,
645
+ rst : input reset,
646
+ i_up : input logic,
647
+ o_count: output logic<32>,
648
+ ) {
649
+ inst u: gray_counter #(WIDTH: 32) (
650
+ i_clk : clk,
651
+ i_rst : rst,
652
+ i_clear : 1'b0,
653
+ i_set : 1'b0,
654
+ i_set_value : 32'b0,
655
+ i_up,
656
+ i_down : 1'b0,
657
+ o_count,
658
+ o_count_next: _,
659
+ o_wrap_around: _,
660
+ );
661
+ }
662
+ `;
663
+
664
+ interface GrayCounterPorts {
665
+ rst: bigint;
666
+ i_up: bigint;
667
+ readonly o_count: bigint;
668
+ }
669
+
670
+ describe("stdlib-gray-counter", () => {
671
+ bench(
672
+ "simulation_build_gray_counter_w32",
673
+ () => {
674
+ const sim = Simulator.fromSource<GrayCounterPorts>(
675
+ GRAY_COUNTER_SRC,
676
+ "Top",
677
+ );
678
+ sim.dispose();
679
+ },
680
+ { iterations: 3, time: 0 },
681
+ );
682
+
683
+ const sim = Simulator.fromSource<GrayCounterPorts>(GRAY_COUNTER_SRC, "Top");
684
+ sim.dut.rst = 1n;
685
+ sim.dut.i_up = 0n;
686
+ sim.tick();
687
+ sim.dut.rst = 0n;
688
+ sim.dut.i_up = 1n;
689
+ sim.tick();
690
+
691
+ afterAll(() => {
692
+ sim.dispose();
693
+ });
694
+
695
+ bench("simulation_tick_gray_counter_w32_x1", () => {
696
+ sim.tick();
697
+ });
698
+
699
+ bench(
700
+ "simulation_tick_gray_counter_w32_x1000000",
701
+ () => {
702
+ for (let i = 0; i < 1_000_000; i++) {
703
+ sim.tick();
704
+ }
705
+ },
706
+ { iterations: 3, time: 0 },
707
+ );
708
+
709
+ bench(
710
+ "testbench_tick_gray_counter_w32_x1000000",
711
+ () => {
712
+ for (let i = 0; i < 1_000_000; i++) {
713
+ sim.tick();
714
+ //read to measure testbench cycle
715
+ sim.dut.o_count;
716
+ }
717
+ },
718
+ { iterations: 3, time: 0 },
719
+ );
403
720
  });