@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/dist/dut.d.ts +1 -1
- package/dist/dut.d.ts.map +1 -1
- package/dist/dut.js +32 -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 +11 -2
- package/dist/napi-helpers.d.ts.map +1 -1
- package/dist/napi-helpers.js +23 -0
- package/dist/napi-helpers.js.map +1 -1
- package/dist/simulation.d.ts.map +1 -1
- package/dist/simulation.js +29 -13
- package/dist/simulation.js.map +1 -1
- package/dist/simulator.d.ts.map +1 -1
- package/dist/simulator.js +28 -12
- package/dist/simulator.js.map +1 -1
- package/dist/types.d.ts +6 -2
- 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 +1040 -661
- package/src/dut.ts +580 -520
- package/src/e2e.bench.ts +627 -310
- package/src/e2e.test.ts +1872 -1550
- package/src/index.ts +46 -46
- package/src/matchers.ts +81 -81
- package/src/napi-bridge.ts +5 -5
- package/src/napi-helpers.ts +475 -400
- package/src/simulation.test.ts +395 -368
- package/src/simulation.ts +502 -430
- package/src/simulator.test.ts +201 -168
- package/src/simulator.ts +317 -267
- package/src/types.ts +102 -98
package/src/e2e.test.ts
CHANGED
|
@@ -9,22 +9,25 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import path from "node:path";
|
|
12
|
-
import {
|
|
13
|
-
import { Simulator } from "./simulator.js";
|
|
14
|
-
import { Simulation } from "./simulation.js";
|
|
12
|
+
import { afterEach, describe, expect, test } from "vitest";
|
|
15
13
|
import { readFourState } from "./dut.js";
|
|
16
|
-
import { X, FourState, SimulationTimeoutError } from "./types.js";
|
|
17
14
|
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
createSimulatorBridge,
|
|
16
|
+
loadNativeAddon,
|
|
17
|
+
parseNapiLayout,
|
|
18
|
+
parseSignalPath,
|
|
19
|
+
type RawNapiAddon,
|
|
20
|
+
type RawNapiSimulatorHandle,
|
|
24
21
|
} from "./napi-helpers.js";
|
|
22
|
+
import { Simulation } from "./simulation.js";
|
|
23
|
+
import { Simulator } from "./simulator.js";
|
|
24
|
+
import { FourState, SimulationTimeoutError, X } from "./types.js";
|
|
25
25
|
|
|
26
26
|
// Fixture project directories
|
|
27
|
-
const FIXTURES_DIR = path.resolve(
|
|
27
|
+
const FIXTURES_DIR = path.resolve(
|
|
28
|
+
import.meta.dirname ?? __dirname,
|
|
29
|
+
"../fixtures",
|
|
30
|
+
);
|
|
28
31
|
const ADDER_PROJECT = path.join(FIXTURES_DIR, "adder");
|
|
29
32
|
const COUNTER_PROJECT = path.join(FIXTURES_DIR, "counter_project");
|
|
30
33
|
const CELOX_TOML_PROJECT = path.join(FIXTURES_DIR, "celox_toml");
|
|
@@ -96,157 +99,157 @@ module Mux4 (
|
|
|
96
99
|
// ---------------------------------------------------------------------------
|
|
97
100
|
|
|
98
101
|
describe("E2E: Simulator.fromSource (event-based)", () => {
|
|
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
|
-
|
|
102
|
+
test("combinational adder: a + b = sum", () => {
|
|
103
|
+
interface AdderPorts {
|
|
104
|
+
rst: bigint;
|
|
105
|
+
a: bigint;
|
|
106
|
+
b: bigint;
|
|
107
|
+
readonly sum: bigint;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const sim = Simulator.fromSource<AdderPorts>(ADDER_SOURCE, "Adder");
|
|
111
|
+
|
|
112
|
+
sim.dut.a = 100n;
|
|
113
|
+
sim.dut.b = 200n;
|
|
114
|
+
sim.tick();
|
|
115
|
+
expect(sim.dut.sum).toBe(300n);
|
|
116
|
+
|
|
117
|
+
sim.dut.a = 0xffffn;
|
|
118
|
+
sim.dut.b = 1n;
|
|
119
|
+
sim.tick();
|
|
120
|
+
expect(sim.dut.sum).toBe(0x10000n);
|
|
121
|
+
|
|
122
|
+
sim.dut.a = 0n;
|
|
123
|
+
sim.dut.b = 0n;
|
|
124
|
+
sim.tick();
|
|
125
|
+
expect(sim.dut.sum).toBe(0n);
|
|
126
|
+
|
|
127
|
+
sim.dispose();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("combinational adder: lazy evalComb on output read", () => {
|
|
131
|
+
interface AdderPorts {
|
|
132
|
+
rst: bigint;
|
|
133
|
+
a: bigint;
|
|
134
|
+
b: bigint;
|
|
135
|
+
readonly sum: bigint;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const sim = Simulator.fromSource<AdderPorts>(ADDER_SOURCE, "Adder");
|
|
139
|
+
|
|
140
|
+
sim.dut.a = 42n;
|
|
141
|
+
sim.dut.b = 58n;
|
|
142
|
+
expect(sim.dut.sum).toBe(100n);
|
|
143
|
+
|
|
144
|
+
sim.dispose();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("sequential counter: counts on clock edges", () => {
|
|
148
|
+
interface CounterPorts {
|
|
149
|
+
rst: bigint;
|
|
150
|
+
en: bigint;
|
|
151
|
+
readonly count: bigint;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const sim = Simulator.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
155
|
+
|
|
156
|
+
// Reset the counter (default async_low: rst=0 is active)
|
|
157
|
+
sim.dut.rst = 0n;
|
|
158
|
+
sim.tick();
|
|
159
|
+
sim.dut.rst = 1n;
|
|
160
|
+
sim.tick();
|
|
161
|
+
expect(sim.dut.count).toBe(0n);
|
|
162
|
+
|
|
163
|
+
// Enable counting
|
|
164
|
+
sim.dut.en = 1n;
|
|
165
|
+
sim.tick();
|
|
166
|
+
expect(sim.dut.count).toBe(1n);
|
|
167
|
+
|
|
168
|
+
sim.tick();
|
|
169
|
+
expect(sim.dut.count).toBe(2n);
|
|
170
|
+
|
|
171
|
+
sim.tick();
|
|
172
|
+
expect(sim.dut.count).toBe(3n);
|
|
173
|
+
|
|
174
|
+
// Disable counting
|
|
175
|
+
sim.dut.en = 0n;
|
|
176
|
+
sim.tick();
|
|
177
|
+
expect(sim.dut.count).toBe(3n);
|
|
178
|
+
|
|
179
|
+
// Re-enable
|
|
180
|
+
sim.dut.en = 1n;
|
|
181
|
+
sim.tick(5);
|
|
182
|
+
expect(sim.dut.count).toBe(8n);
|
|
183
|
+
|
|
184
|
+
sim.dispose();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("always_comb evaluated before FF on tick (no prior output read)", () => {
|
|
188
|
+
interface CounterPorts {
|
|
189
|
+
rst: number;
|
|
190
|
+
en: number;
|
|
191
|
+
readonly count: number;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const sim = Simulator.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
195
|
+
|
|
196
|
+
// Reset (default async_low: rst=0 is active)
|
|
197
|
+
sim.dut.rst = 0;
|
|
198
|
+
sim.tick();
|
|
199
|
+
sim.dut.rst = 1;
|
|
200
|
+
sim.tick();
|
|
201
|
+
|
|
202
|
+
// Write input and tick WITHOUT reading any output first.
|
|
203
|
+
// This catches the stale-comb bug where always_ff reads stale
|
|
204
|
+
// combinational values because eval_comb was skipped before FF.
|
|
205
|
+
sim.dut.en = 1;
|
|
206
|
+
sim.tick();
|
|
207
|
+
expect(Number(sim.dut.count)).toBe(1);
|
|
208
|
+
|
|
209
|
+
// Again: set input, tick, verify — no intermediate reads
|
|
210
|
+
sim.dut.en = 1;
|
|
211
|
+
sim.tick();
|
|
212
|
+
expect(Number(sim.dut.count)).toBe(2);
|
|
213
|
+
|
|
214
|
+
// Disable, tick, verify
|
|
215
|
+
sim.dut.en = 0;
|
|
216
|
+
sim.tick();
|
|
217
|
+
expect(Number(sim.dut.count)).toBe(2);
|
|
218
|
+
|
|
219
|
+
sim.dispose();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("combinational multiplexer", () => {
|
|
223
|
+
interface Mux4Ports {
|
|
224
|
+
sel: bigint;
|
|
225
|
+
d0: bigint;
|
|
226
|
+
d1: bigint;
|
|
227
|
+
d2: bigint;
|
|
228
|
+
d3: bigint;
|
|
229
|
+
readonly y: bigint;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const sim = Simulator.fromSource<Mux4Ports>(MULTIPLEXER_SOURCE, "Mux4");
|
|
233
|
+
|
|
234
|
+
sim.dut.d0 = 0xaan;
|
|
235
|
+
sim.dut.d1 = 0xbbn;
|
|
236
|
+
sim.dut.d2 = 0xccn;
|
|
237
|
+
sim.dut.d3 = 0xddn;
|
|
238
|
+
|
|
239
|
+
sim.dut.sel = 0n;
|
|
240
|
+
expect(sim.dut.y).toBe(0xaan);
|
|
241
|
+
|
|
242
|
+
sim.dut.sel = 1n;
|
|
243
|
+
expect(sim.dut.y).toBe(0xbbn);
|
|
244
|
+
|
|
245
|
+
sim.dut.sel = 2n;
|
|
246
|
+
expect(sim.dut.y).toBe(0xccn);
|
|
247
|
+
|
|
248
|
+
sim.dut.sel = 3n;
|
|
249
|
+
expect(sim.dut.y).toBe(0xddn);
|
|
250
|
+
|
|
251
|
+
sim.dispose();
|
|
252
|
+
});
|
|
250
253
|
});
|
|
251
254
|
|
|
252
255
|
// ---------------------------------------------------------------------------
|
|
@@ -254,32 +257,32 @@ describe("E2E: Simulator.fromSource (event-based)", () => {
|
|
|
254
257
|
// ---------------------------------------------------------------------------
|
|
255
258
|
|
|
256
259
|
describe("E2E: Simulation.fromSource (time-based)", () => {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
test("counter with timed clock: step-by-step", () => {
|
|
261
|
+
interface CounterPorts {
|
|
262
|
+
rst: bigint;
|
|
263
|
+
en: bigint;
|
|
264
|
+
readonly count: bigint;
|
|
265
|
+
}
|
|
263
266
|
|
|
264
|
-
|
|
267
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
265
268
|
|
|
266
|
-
|
|
267
|
-
|
|
269
|
+
sim.addClock("clk", { period: 10 });
|
|
270
|
+
expect(sim.time()).toBe(0);
|
|
268
271
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
272
|
+
// Reset (default async_low: rst=0 is active)
|
|
273
|
+
sim.dut.rst = 0n;
|
|
274
|
+
sim.runUntil(20);
|
|
275
|
+
sim.dut.rst = 1n;
|
|
276
|
+
sim.dut.en = 1n;
|
|
274
277
|
|
|
275
|
-
|
|
278
|
+
sim.runUntil(100);
|
|
276
279
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
+
const count = sim.dut.count;
|
|
281
|
+
expect(count).toBeGreaterThan(0n);
|
|
282
|
+
expect(sim.time()).toBe(100);
|
|
280
283
|
|
|
281
|
-
|
|
282
|
-
|
|
284
|
+
sim.dispose();
|
|
285
|
+
});
|
|
283
286
|
});
|
|
284
287
|
|
|
285
288
|
// ---------------------------------------------------------------------------
|
|
@@ -287,58 +290,58 @@ describe("E2E: Simulation.fromSource (time-based)", () => {
|
|
|
287
290
|
// ---------------------------------------------------------------------------
|
|
288
291
|
|
|
289
292
|
describe("E2E: Simulator.fromProject (event-based)", () => {
|
|
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
|
-
|
|
293
|
+
test("combinational adder from project directory", () => {
|
|
294
|
+
interface AdderPorts {
|
|
295
|
+
rst: bigint;
|
|
296
|
+
a: bigint;
|
|
297
|
+
b: bigint;
|
|
298
|
+
readonly sum: bigint;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const sim = Simulator.fromProject<AdderPorts>(ADDER_PROJECT, "Adder");
|
|
302
|
+
|
|
303
|
+
sim.dut.a = 100n;
|
|
304
|
+
sim.dut.b = 200n;
|
|
305
|
+
sim.tick();
|
|
306
|
+
expect(sim.dut.sum).toBe(300n);
|
|
307
|
+
|
|
308
|
+
sim.dut.a = 0xffffn;
|
|
309
|
+
sim.dut.b = 1n;
|
|
310
|
+
sim.tick();
|
|
311
|
+
expect(sim.dut.sum).toBe(0x10000n);
|
|
312
|
+
|
|
313
|
+
sim.dispose();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test("sequential counter from project directory", () => {
|
|
317
|
+
interface CounterPorts {
|
|
318
|
+
rst: bigint;
|
|
319
|
+
en: bigint;
|
|
320
|
+
readonly count: bigint;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const sim = Simulator.fromProject<CounterPorts>(COUNTER_PROJECT, "Counter");
|
|
324
|
+
|
|
325
|
+
// Reset the counter (default async_low: rst=0 is active)
|
|
326
|
+
sim.dut.rst = 0n;
|
|
327
|
+
sim.tick();
|
|
328
|
+
sim.dut.rst = 1n;
|
|
329
|
+
sim.tick();
|
|
330
|
+
expect(sim.dut.count).toBe(0n);
|
|
331
|
+
|
|
332
|
+
// Enable counting
|
|
333
|
+
sim.dut.en = 1n;
|
|
334
|
+
sim.tick();
|
|
335
|
+
expect(sim.dut.count).toBe(1n);
|
|
336
|
+
|
|
337
|
+
sim.tick();
|
|
338
|
+
expect(sim.dut.count).toBe(2n);
|
|
339
|
+
|
|
340
|
+
sim.tick();
|
|
341
|
+
expect(sim.dut.count).toBe(3n);
|
|
342
|
+
|
|
343
|
+
sim.dispose();
|
|
344
|
+
});
|
|
342
345
|
});
|
|
343
346
|
|
|
344
347
|
// ---------------------------------------------------------------------------
|
|
@@ -346,32 +349,35 @@ describe("E2E: Simulator.fromProject (event-based)", () => {
|
|
|
346
349
|
// ---------------------------------------------------------------------------
|
|
347
350
|
|
|
348
351
|
describe("E2E: Simulation.fromProject (time-based)", () => {
|
|
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
|
-
|
|
352
|
+
test("counter with timed clock from project directory", () => {
|
|
353
|
+
interface CounterPorts {
|
|
354
|
+
rst: bigint;
|
|
355
|
+
en: bigint;
|
|
356
|
+
readonly count: bigint;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const sim = Simulation.fromProject<CounterPorts>(
|
|
360
|
+
COUNTER_PROJECT,
|
|
361
|
+
"Counter",
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
sim.addClock("clk", { period: 10 });
|
|
365
|
+
expect(sim.time()).toBe(0);
|
|
366
|
+
|
|
367
|
+
// Reset (default async_low: rst=0 is active)
|
|
368
|
+
sim.dut.rst = 0n;
|
|
369
|
+
sim.runUntil(20);
|
|
370
|
+
sim.dut.rst = 1n;
|
|
371
|
+
sim.dut.en = 1n;
|
|
372
|
+
|
|
373
|
+
sim.runUntil(100);
|
|
374
|
+
|
|
375
|
+
const count = sim.dut.count;
|
|
376
|
+
expect(count).toBeGreaterThan(0n);
|
|
377
|
+
expect(sim.time()).toBe(100);
|
|
378
|
+
|
|
379
|
+
sim.dispose();
|
|
380
|
+
});
|
|
375
381
|
});
|
|
376
382
|
|
|
377
383
|
// ---------------------------------------------------------------------------
|
|
@@ -379,41 +385,41 @@ describe("E2E: Simulation.fromProject (time-based)", () => {
|
|
|
379
385
|
// ---------------------------------------------------------------------------
|
|
380
386
|
|
|
381
387
|
describe("E2E: Simulator.create (backward compat)", () => {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
388
|
+
test("combinational adder via Simulator.create()", () => {
|
|
389
|
+
interface AdderPorts {
|
|
390
|
+
rst: bigint;
|
|
391
|
+
a: bigint;
|
|
392
|
+
b: bigint;
|
|
393
|
+
readonly sum: bigint;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const addon = loadNativeAddon();
|
|
397
|
+
const nativeCreateSimulator = createSimulatorBridge(addon);
|
|
398
|
+
|
|
399
|
+
const sim = Simulator.create<AdderPorts>(
|
|
400
|
+
{
|
|
401
|
+
__celox_module: true,
|
|
402
|
+
name: "Adder",
|
|
403
|
+
source: ADDER_SOURCE,
|
|
404
|
+
ports: {
|
|
405
|
+
clk: { direction: "input", type: "clock", width: 1 },
|
|
406
|
+
rst: { direction: "input", type: "reset", width: 1 },
|
|
407
|
+
a: { direction: "input", type: "logic", width: 16 },
|
|
408
|
+
b: { direction: "input", type: "logic", width: 16 },
|
|
409
|
+
sum: { direction: "output", type: "logic", width: 17 },
|
|
410
|
+
},
|
|
411
|
+
events: ["clk"],
|
|
412
|
+
},
|
|
413
|
+
{ __nativeCreate: nativeCreateSimulator },
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
sim.dut.a = 100n;
|
|
417
|
+
sim.dut.b = 200n;
|
|
418
|
+
sim.tick();
|
|
419
|
+
expect(sim.dut.sum).toBe(300n);
|
|
420
|
+
|
|
421
|
+
sim.dispose();
|
|
422
|
+
});
|
|
417
423
|
});
|
|
418
424
|
|
|
419
425
|
// ---------------------------------------------------------------------------
|
|
@@ -439,7 +445,7 @@ module LogicBitMix (
|
|
|
439
445
|
y_logic_from_bit: output logic<8>,
|
|
440
446
|
y_bit_from_logic: output bit<8>,
|
|
441
447
|
) {
|
|
442
|
-
assign y_bit_from_logic = a_logic;
|
|
448
|
+
assign y_bit_from_logic = a_logic as u8;
|
|
443
449
|
assign y_logic_from_bit = b_bit;
|
|
444
450
|
}
|
|
445
451
|
`;
|
|
@@ -472,325 +478,347 @@ module Adder4S (
|
|
|
472
478
|
`;
|
|
473
479
|
|
|
474
480
|
describe("E2E: 4-state simulation", () => {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
481
|
+
let raw: RawNapiSimulatorHandle | undefined;
|
|
482
|
+
let addon: RawNapiAddon;
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
addon = loadNativeAddon();
|
|
486
|
+
} catch (e) {
|
|
487
|
+
throw new Error(`Failed to load NAPI addon for 4-state tests: ${e}`);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
afterEach(() => {
|
|
491
|
+
raw?.dispose();
|
|
492
|
+
raw = undefined;
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
test("initial values: logic ports start as X, bit ports start as 0", () => {
|
|
496
|
+
const source = `
|
|
491
497
|
module InitTest (
|
|
492
498
|
a: input logic<8>,
|
|
493
499
|
b: input bit<8>,
|
|
494
500
|
) {}
|
|
495
501
|
`;
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
502
|
+
raw = new addon.NativeSimulatorHandle(source, "InitTest", {
|
|
503
|
+
fourState: true,
|
|
504
|
+
});
|
|
505
|
+
const layout = parseNapiLayout(raw.layoutJson);
|
|
506
|
+
const buf = raw.sharedMemory().buffer;
|
|
507
|
+
|
|
508
|
+
// logic port should have mask=0xFF (all X), X encoding: v=1, m=1
|
|
509
|
+
const [valA, maskA] = readFourState(buf, layout.forDut.a);
|
|
510
|
+
expect(valA).toBe(0xffn);
|
|
511
|
+
expect(maskA).toBe(0xffn);
|
|
512
|
+
|
|
513
|
+
// bit port should have mask=0 (defined)
|
|
514
|
+
// bit is not 4-state, so no mask — reading its value should be 0
|
|
515
|
+
expect(layout.forDut.b.is4state).toBe(false);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
test("writing X clears value and sets mask", () => {
|
|
519
|
+
interface Ports {
|
|
520
|
+
a: bigint;
|
|
521
|
+
readonly y_and: bigint;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const sim = Simulator.fromSource<Ports>(AND_OR_SOURCE, "AndOr", {
|
|
525
|
+
fourState: true,
|
|
526
|
+
});
|
|
527
|
+
raw = undefined; // sim manages its own handle
|
|
528
|
+
|
|
529
|
+
// Write X to input 'a' via DUT
|
|
530
|
+
(sim.dut as any).a = X;
|
|
531
|
+
|
|
532
|
+
// We can't inspect mask through DUT getter (it only returns value),
|
|
533
|
+
// so this test verifies X write doesn't throw and propagation works.
|
|
534
|
+
// For detailed mask inspection, see the raw NAPI tests below.
|
|
535
|
+
sim.dispose();
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
test("AND: 0 & X = 0 (dominant zero)", () => {
|
|
539
|
+
raw = new addon.NativeSimulatorHandle(AND_OR_SOURCE, "AndOr", {
|
|
540
|
+
fourState: true,
|
|
541
|
+
});
|
|
542
|
+
const layout = parseNapiLayout(raw.layoutJson);
|
|
543
|
+
const buf = raw.sharedMemory().buffer;
|
|
544
|
+
const view = new DataView(buf);
|
|
545
|
+
const _events: Record<string, number> = JSON.parse(raw.eventsJson);
|
|
546
|
+
|
|
547
|
+
const sigA = layout.forDut.a;
|
|
548
|
+
const sigB = layout.forDut.b;
|
|
549
|
+
const sigYAnd = layout.forDut.y_and;
|
|
550
|
+
const sigYOr = layout.forDut.y_or;
|
|
551
|
+
|
|
552
|
+
// a = 0 (value=0, mask=0)
|
|
553
|
+
view.setUint8(sigA.offset, 0);
|
|
554
|
+
view.setUint8(sigA.offset + sigA.byteSize, 0);
|
|
555
|
+
|
|
556
|
+
// b = X (value=0, mask=1)
|
|
557
|
+
view.setUint8(sigB.offset, 0);
|
|
558
|
+
view.setUint8(sigB.offset + sigB.byteSize, 1);
|
|
559
|
+
|
|
560
|
+
raw.evalComb();
|
|
561
|
+
|
|
562
|
+
// 0 & X = 0 (mask should be 0 — dominant zero)
|
|
563
|
+
const [vAnd, mAnd] = readFourState(buf, sigYAnd);
|
|
564
|
+
expect(vAnd).toBe(0n);
|
|
565
|
+
expect(mAnd).toBe(0n);
|
|
566
|
+
|
|
567
|
+
// 0 | X = X (mask should be 1)
|
|
568
|
+
const [vOr, mOr] = readFourState(buf, sigYOr);
|
|
569
|
+
expect(vOr).toBe(0n);
|
|
570
|
+
expect(mOr).toBe(1n);
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
test("OR: 1 | X = 1 (dominant one)", () => {
|
|
574
|
+
raw = new addon.NativeSimulatorHandle(AND_OR_SOURCE, "AndOr", {
|
|
575
|
+
fourState: true,
|
|
576
|
+
});
|
|
577
|
+
const layout = parseNapiLayout(raw.layoutJson);
|
|
578
|
+
const buf = raw.sharedMemory().buffer;
|
|
579
|
+
const view = new DataView(buf);
|
|
580
|
+
|
|
581
|
+
const sigA = layout.forDut.a;
|
|
582
|
+
const sigB = layout.forDut.b;
|
|
583
|
+
const sigYOr = layout.forDut.y_or;
|
|
584
|
+
|
|
585
|
+
// a = 1 (value=1, mask=0)
|
|
586
|
+
view.setUint8(sigA.offset, 1);
|
|
587
|
+
view.setUint8(sigA.offset + sigA.byteSize, 0);
|
|
588
|
+
|
|
589
|
+
// b = X (value=0, mask=1)
|
|
590
|
+
view.setUint8(sigB.offset, 0);
|
|
591
|
+
view.setUint8(sigB.offset + sigB.byteSize, 1);
|
|
592
|
+
|
|
593
|
+
raw.evalComb();
|
|
594
|
+
|
|
595
|
+
// 1 | X = 1 (mask should be 0 — dominant one)
|
|
596
|
+
const [vOr, mOr] = readFourState(buf, sigYOr);
|
|
597
|
+
expect(vOr).toBe(1n);
|
|
598
|
+
expect(mOr).toBe(0n);
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
test("logic-to-bit assignment strips X mask", () => {
|
|
602
|
+
raw = new addon.NativeSimulatorHandle(LOGIC_BIT_MIX_SOURCE, "LogicBitMix", {
|
|
603
|
+
fourState: true,
|
|
604
|
+
});
|
|
605
|
+
const layout = parseNapiLayout(raw.layoutJson);
|
|
606
|
+
const buf = raw.sharedMemory().buffer;
|
|
607
|
+
const view = new DataView(buf);
|
|
608
|
+
|
|
609
|
+
const sigALogic = layout.forDut.a_logic;
|
|
610
|
+
const sigYBitFromLogic = layout.forDut.y_bit_from_logic;
|
|
611
|
+
|
|
612
|
+
// a_logic = all-X (value=0, mask=0xFF)
|
|
613
|
+
view.setUint8(sigALogic.offset, 0);
|
|
614
|
+
view.setUint8(sigALogic.offset + sigALogic.byteSize, 0xff);
|
|
615
|
+
|
|
616
|
+
raw.evalComb();
|
|
617
|
+
|
|
618
|
+
// y_bit_from_logic is bit type — X should be stripped (mask=0)
|
|
619
|
+
expect(sigYBitFromLogic.is4state).toBe(false);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
test("bit-to-logic assignment has no X", () => {
|
|
623
|
+
raw = new addon.NativeSimulatorHandle(LOGIC_BIT_MIX_SOURCE, "LogicBitMix", {
|
|
624
|
+
fourState: true,
|
|
625
|
+
});
|
|
626
|
+
const layout = parseNapiLayout(raw.layoutJson);
|
|
627
|
+
const buf = raw.sharedMemory().buffer;
|
|
628
|
+
const view = new DataView(buf);
|
|
629
|
+
|
|
630
|
+
const sigBBit = layout.forDut.b_bit;
|
|
631
|
+
const sigYLogicFromBit = layout.forDut.y_logic_from_bit;
|
|
632
|
+
|
|
633
|
+
// b_bit = 0xAA (bit type, always defined)
|
|
634
|
+
view.setUint8(sigBBit.offset, 0xaa);
|
|
635
|
+
|
|
636
|
+
raw.evalComb();
|
|
637
|
+
|
|
638
|
+
// y_logic_from_bit should be 0xAA with mask=0
|
|
639
|
+
const [vLogic, mLogic] = readFourState(buf, sigYLogicFromBit);
|
|
640
|
+
expect(vLogic).toBe(0xaan);
|
|
641
|
+
expect(mLogic).toBe(0n);
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
test("arithmetic with X produces all-X output", () => {
|
|
645
|
+
raw = new addon.NativeSimulatorHandle(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
646
|
+
fourState: true,
|
|
647
|
+
});
|
|
648
|
+
const layout = parseNapiLayout(raw.layoutJson);
|
|
649
|
+
const buf = raw.sharedMemory().buffer;
|
|
650
|
+
const view = new DataView(buf);
|
|
651
|
+
|
|
652
|
+
const sigA = layout.forDut.a;
|
|
653
|
+
const sigB = layout.forDut.b;
|
|
654
|
+
const sigY = layout.forDut.y;
|
|
655
|
+
|
|
656
|
+
// a = 42 (defined), b = X (all X)
|
|
657
|
+
view.setUint8(sigA.offset, 42);
|
|
658
|
+
view.setUint8(sigA.offset + sigA.byteSize, 0); // mask=0
|
|
659
|
+
|
|
660
|
+
view.setUint8(sigB.offset, 0);
|
|
661
|
+
view.setUint8(sigB.offset + sigB.byteSize, 0xff); // mask=0xFF
|
|
662
|
+
|
|
663
|
+
raw.evalComb();
|
|
664
|
+
|
|
665
|
+
// a + X = all-X
|
|
666
|
+
const [, mY] = readFourState(buf, sigY);
|
|
667
|
+
expect(mY).toBe(0xffn);
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
test("defined inputs in 4-state mode behave like 2-state", () => {
|
|
671
|
+
raw = new addon.NativeSimulatorHandle(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
672
|
+
fourState: true,
|
|
673
|
+
});
|
|
674
|
+
const layout = parseNapiLayout(raw.layoutJson);
|
|
675
|
+
const buf = raw.sharedMemory().buffer;
|
|
676
|
+
const view = new DataView(buf);
|
|
677
|
+
|
|
678
|
+
const sigA = layout.forDut.a;
|
|
679
|
+
const sigB = layout.forDut.b;
|
|
680
|
+
const sigY = layout.forDut.y;
|
|
681
|
+
|
|
682
|
+
// a = 100 (defined), b = 55 (defined)
|
|
683
|
+
view.setUint8(sigA.offset, 100);
|
|
684
|
+
view.setUint8(sigA.offset + sigA.byteSize, 0);
|
|
685
|
+
|
|
686
|
+
view.setUint8(sigB.offset, 55);
|
|
687
|
+
view.setUint8(sigB.offset + sigB.byteSize, 0);
|
|
688
|
+
|
|
689
|
+
raw.evalComb();
|
|
690
|
+
|
|
691
|
+
const [vY, mY] = readFourState(buf, sigY);
|
|
692
|
+
expect(vY).toBe(155n);
|
|
693
|
+
expect(mY).toBe(0n);
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
test("FF captures X from input, reset clears X", () => {
|
|
697
|
+
raw = new addon.NativeSimulatorHandle(FF_SOURCE, "FF", { fourState: true });
|
|
698
|
+
const layout = parseNapiLayout(raw.layoutJson);
|
|
699
|
+
const buf = raw.sharedMemory().buffer;
|
|
700
|
+
const view = new DataView(buf);
|
|
701
|
+
const events: Record<string, number> = JSON.parse(raw.eventsJson);
|
|
702
|
+
|
|
703
|
+
const sigRst = layout.forDut.rst;
|
|
704
|
+
const sigD = layout.forDut.d;
|
|
705
|
+
const sigQ = layout.forDut.q;
|
|
706
|
+
const clkEventId = events.clk;
|
|
707
|
+
|
|
708
|
+
// 1. Assert reset (default async_low: rst=0 is active), d=X
|
|
709
|
+
view.setUint8(sigRst.offset, 0);
|
|
710
|
+
view.setUint8(sigRst.offset + sigRst.byteSize, 0); // rst is defined
|
|
711
|
+
|
|
712
|
+
view.setUint8(sigD.offset, 0);
|
|
713
|
+
view.setUint8(sigD.offset + sigD.byteSize, 0xff); // d = all-X
|
|
714
|
+
|
|
715
|
+
raw.tick(clkEventId);
|
|
716
|
+
|
|
717
|
+
// After reset, q should be 0 with mask=0
|
|
718
|
+
const [vQ1, mQ1] = readFourState(buf, sigQ);
|
|
719
|
+
expect(vQ1).toBe(0n);
|
|
720
|
+
expect(mQ1).toBe(0n);
|
|
721
|
+
|
|
722
|
+
// 2. Release reset (rst=1 is inactive), d = partial X (value=0xA5, mask=0x0F)
|
|
723
|
+
view.setUint8(sigRst.offset, 1);
|
|
724
|
+
view.setUint8(sigRst.offset + sigRst.byteSize, 0);
|
|
725
|
+
|
|
726
|
+
view.setUint8(sigD.offset, 0xa5);
|
|
727
|
+
view.setUint8(sigD.offset + sigD.byteSize, 0x0f);
|
|
728
|
+
|
|
729
|
+
raw.tick(clkEventId);
|
|
730
|
+
|
|
731
|
+
// FF should capture X mask from d
|
|
732
|
+
const [, mQ2] = readFourState(buf, sigQ);
|
|
733
|
+
expect(mQ2).toBe(0x0fn);
|
|
734
|
+
|
|
735
|
+
// 3. Assert reset again (rst=0): should clear X
|
|
736
|
+
view.setUint8(sigRst.offset, 0);
|
|
737
|
+
view.setUint8(sigRst.offset + sigRst.byteSize, 0);
|
|
738
|
+
|
|
739
|
+
raw.tick(clkEventId);
|
|
740
|
+
|
|
741
|
+
const [vQ3, mQ3] = readFourState(buf, sigQ);
|
|
742
|
+
expect(vQ3).toBe(0n);
|
|
743
|
+
expect(mQ3).toBe(0n);
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
test("FourState write through DUT sets value and mask", () => {
|
|
747
|
+
raw = new addon.NativeSimulatorHandle(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
748
|
+
fourState: true,
|
|
749
|
+
});
|
|
750
|
+
const layout = parseNapiLayout(raw.layoutJson);
|
|
751
|
+
const buf = raw.sharedMemory().buffer;
|
|
752
|
+
const view = new DataView(buf);
|
|
753
|
+
|
|
754
|
+
const sigA = layout.forDut.a;
|
|
755
|
+
|
|
756
|
+
// Write via DUT-style: FourState(0b1010_0101, 0b0000_1111)
|
|
757
|
+
// value=0xA5, mask=0x0F means lower 4 bits are X
|
|
758
|
+
view.setUint8(sigA.offset, 0xa5);
|
|
759
|
+
view.setUint8(sigA.offset + sigA.byteSize, 0x0f);
|
|
760
|
+
|
|
761
|
+
const [vA, mA] = readFourState(buf, sigA);
|
|
762
|
+
expect(vA).toBe(0xa5n);
|
|
763
|
+
expect(mA).toBe(0x0fn);
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
test("setting defined value clears X mask", () => {
|
|
767
|
+
raw = new addon.NativeSimulatorHandle(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
768
|
+
fourState: true,
|
|
769
|
+
});
|
|
770
|
+
const layout = parseNapiLayout(raw.layoutJson);
|
|
771
|
+
const buf = raw.sharedMemory().buffer;
|
|
772
|
+
const view = new DataView(buf);
|
|
773
|
+
|
|
774
|
+
const sigA = layout.forDut.a;
|
|
775
|
+
|
|
776
|
+
// Start with X
|
|
777
|
+
view.setUint8(sigA.offset, 0);
|
|
778
|
+
view.setUint8(sigA.offset + sigA.byteSize, 0xff);
|
|
779
|
+
|
|
780
|
+
const [, mBefore] = readFourState(buf, sigA);
|
|
781
|
+
expect(mBefore).toBe(0xffn);
|
|
782
|
+
|
|
783
|
+
// Write a defined value (clear mask)
|
|
784
|
+
view.setUint8(sigA.offset, 42);
|
|
785
|
+
view.setUint8(sigA.offset + sigA.byteSize, 0);
|
|
786
|
+
|
|
787
|
+
const [vAfter, mAfter] = readFourState(buf, sigA);
|
|
788
|
+
expect(vAfter).toBe(42n);
|
|
789
|
+
expect(mAfter).toBe(0n);
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
test("4-state through DUT high-level API (fromSource with fourState)", () => {
|
|
793
|
+
interface Ports {
|
|
794
|
+
a: bigint;
|
|
795
|
+
b: bigint;
|
|
796
|
+
readonly y: bigint;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
const sim = Simulator.fromSource<Ports>(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
800
|
+
fourState: true,
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
// Write defined values — should behave like 2-state
|
|
804
|
+
sim.dut.a = 100n;
|
|
805
|
+
sim.dut.b = 55n;
|
|
806
|
+
expect(sim.dut.y).toBe(155n);
|
|
807
|
+
|
|
808
|
+
// Write X to a — output should propagate X (value reads as 0)
|
|
809
|
+
(sim.dut as any).a = X;
|
|
810
|
+
// After writing X, the value part of 'y' is implementation-defined
|
|
811
|
+
// but the read should not throw
|
|
812
|
+
const _yVal = sim.dut.y;
|
|
813
|
+
expect(typeof _yVal).toBe("bigint");
|
|
814
|
+
|
|
815
|
+
// Write FourState with partial X
|
|
816
|
+
(sim.dut as any).a = FourState(0xa0, 0x0f);
|
|
817
|
+
const _yVal2 = sim.dut.y;
|
|
818
|
+
expect(typeof _yVal2).toBe("bigint");
|
|
819
|
+
|
|
820
|
+
sim.dispose();
|
|
821
|
+
});
|
|
794
822
|
});
|
|
795
823
|
|
|
796
824
|
// ---------------------------------------------------------------------------
|
|
@@ -798,127 +826,133 @@ module InitTest (
|
|
|
798
826
|
// ---------------------------------------------------------------------------
|
|
799
827
|
|
|
800
828
|
describe("E2E: 4-state high-level DUT API", () => {
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
829
|
+
test("counter in 4-state mode: reset clears X, counting works", () => {
|
|
830
|
+
interface CounterPorts {
|
|
831
|
+
rst: bigint;
|
|
832
|
+
en: bigint;
|
|
833
|
+
readonly count: bigint;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
const sim = Simulator.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter", {
|
|
837
|
+
fourState: true,
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
// In 4-state mode, count starts as X. Reset should clear it.
|
|
841
|
+
// (default async_low: rst=0 is active)
|
|
842
|
+
sim.dut.rst = 0n;
|
|
843
|
+
// Clear X on en before deactivating reset. In 4-state mode, en starts
|
|
844
|
+
// as X (v=1, m=1). The FF branch condition currently uses only the value
|
|
845
|
+
// bit, so X with v=1 is treated as "true" — a known limitation in FF
|
|
846
|
+
// conditional handling (combinational mux handles X correctly).
|
|
847
|
+
sim.dut.en = 0n;
|
|
848
|
+
sim.tick();
|
|
849
|
+
sim.dut.rst = 1n;
|
|
850
|
+
sim.tick();
|
|
851
|
+
expect(sim.dut.count).toBe(0n);
|
|
852
|
+
|
|
853
|
+
// Enable counting — should work exactly like 2-state
|
|
854
|
+
sim.dut.en = 1n;
|
|
855
|
+
sim.tick();
|
|
856
|
+
expect(sim.dut.count).toBe(1n);
|
|
857
|
+
|
|
858
|
+
sim.tick();
|
|
859
|
+
expect(sim.dut.count).toBe(2n);
|
|
860
|
+
|
|
861
|
+
sim.tick();
|
|
862
|
+
expect(sim.dut.count).toBe(3n);
|
|
863
|
+
|
|
864
|
+
sim.dispose();
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
test("multiplexer with X selector produces X output", () => {
|
|
868
|
+
const addon = loadNativeAddon();
|
|
869
|
+
const raw = new addon.NativeSimulatorHandle(MULTIPLEXER_SOURCE, "Mux4", {
|
|
870
|
+
fourState: true,
|
|
871
|
+
});
|
|
872
|
+
const layout = parseNapiLayout(raw.layoutJson);
|
|
873
|
+
const buf = raw.sharedMemory().buffer;
|
|
874
|
+
const view = new DataView(buf);
|
|
875
|
+
|
|
876
|
+
const sigSel = layout.forDut.sel;
|
|
877
|
+
const sigD0 = layout.forDut.d0;
|
|
878
|
+
const sigY = layout.forDut.y;
|
|
879
|
+
|
|
880
|
+
// Set d0 = 0xAA (defined)
|
|
881
|
+
view.setUint8(sigD0.offset, 0xaa);
|
|
882
|
+
view.setUint8(sigD0.offset + sigD0.byteSize, 0);
|
|
883
|
+
|
|
884
|
+
// Set sel = X
|
|
885
|
+
view.setUint8(sigSel.offset, 0);
|
|
886
|
+
view.setUint8(sigSel.offset + sigSel.byteSize, 0x03);
|
|
887
|
+
|
|
888
|
+
raw.evalComb();
|
|
889
|
+
|
|
890
|
+
// With X selector, output should be all-X
|
|
891
|
+
const [, mY2] = readFourState(buf, sigY);
|
|
892
|
+
expect(mY2).toBe(0xffn);
|
|
893
|
+
|
|
894
|
+
raw.dispose();
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
test("FF via DUT API: write X input, tick, read output", () => {
|
|
898
|
+
interface FFPorts {
|
|
899
|
+
rst: bigint;
|
|
900
|
+
d: bigint;
|
|
901
|
+
readonly q: bigint;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
const sim = Simulator.fromSource<FFPorts>(FF_SOURCE, "FF", {
|
|
905
|
+
fourState: true,
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
// Reset to clear initial X (default async_low: rst=0 is active)
|
|
909
|
+
sim.dut.rst = 0n;
|
|
910
|
+
sim.tick();
|
|
911
|
+
sim.dut.rst = 1n;
|
|
912
|
+
expect(sim.dut.q).toBe(0n);
|
|
913
|
+
|
|
914
|
+
// Write a defined value
|
|
915
|
+
sim.dut.d = 0x42n;
|
|
916
|
+
sim.tick();
|
|
917
|
+
expect(sim.dut.q).toBe(0x42n);
|
|
918
|
+
|
|
919
|
+
// Write X to d, tick — q should capture it (value read still returns a bigint)
|
|
920
|
+
(sim.dut as any).d = X;
|
|
921
|
+
sim.tick();
|
|
922
|
+
expect(typeof sim.dut.q).toBe("bigint");
|
|
923
|
+
|
|
924
|
+
// Write defined value again — q should recover
|
|
925
|
+
sim.dut.d = 0x99n;
|
|
926
|
+
sim.tick();
|
|
927
|
+
expect(sim.dut.q).toBe(0x99n);
|
|
928
|
+
|
|
929
|
+
sim.dispose();
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
test("X to defined transition: adder recovers from X", () => {
|
|
933
|
+
interface Ports {
|
|
934
|
+
a: bigint;
|
|
935
|
+
b: bigint;
|
|
936
|
+
readonly y: bigint;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
const sim = Simulator.fromSource<Ports>(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
940
|
+
fourState: true,
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
// Start with X
|
|
944
|
+
(sim.dut as any).a = X;
|
|
945
|
+
sim.dut.b = 10n;
|
|
946
|
+
// Output has X — just verify it doesn't crash
|
|
947
|
+
expect(typeof sim.dut.y).toBe("bigint");
|
|
948
|
+
|
|
949
|
+
// Clear X by writing defined values
|
|
950
|
+
sim.dut.a = 20n;
|
|
951
|
+
sim.dut.b = 30n;
|
|
952
|
+
expect(sim.dut.y).toBe(50n);
|
|
953
|
+
|
|
954
|
+
sim.dispose();
|
|
955
|
+
});
|
|
922
956
|
});
|
|
923
957
|
|
|
924
958
|
// ---------------------------------------------------------------------------
|
|
@@ -926,85 +960,87 @@ describe("E2E: 4-state high-level DUT API", () => {
|
|
|
926
960
|
// ---------------------------------------------------------------------------
|
|
927
961
|
|
|
928
962
|
describe("E2E: 4-state Simulation (time-based)", () => {
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
963
|
+
test("FF with clock-driven 4-state: reset clears X, captures defined values", () => {
|
|
964
|
+
interface FFPorts {
|
|
965
|
+
rst: bigint;
|
|
966
|
+
d: bigint;
|
|
967
|
+
readonly q: bigint;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
const sim = Simulation.fromSource<FFPorts>(FF_SOURCE, "FF", {
|
|
971
|
+
fourState: true,
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
sim.addClock("clk", { period: 10 });
|
|
975
|
+
expect(sim.time()).toBe(0);
|
|
976
|
+
|
|
977
|
+
// Reset to clear initial X on q (default async_low: rst=0 is active)
|
|
978
|
+
sim.dut.rst = 0n;
|
|
979
|
+
sim.runUntil(20);
|
|
980
|
+
sim.dut.rst = 1n;
|
|
981
|
+
expect(sim.dut.q).toBe(0n);
|
|
982
|
+
|
|
983
|
+
// Drive d with defined value
|
|
984
|
+
sim.dut.d = 0x55n;
|
|
985
|
+
sim.runUntil(40);
|
|
986
|
+
expect(sim.dut.q).toBe(0x55n);
|
|
987
|
+
|
|
988
|
+
// Drive d with different value
|
|
989
|
+
sim.dut.d = 0xaan;
|
|
990
|
+
sim.runUntil(60);
|
|
991
|
+
expect(sim.dut.q).toBe(0xaan);
|
|
992
|
+
|
|
993
|
+
sim.dispose();
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
test("counter in 4-state time-based mode", () => {
|
|
997
|
+
interface CounterPorts {
|
|
998
|
+
rst: bigint;
|
|
999
|
+
en: bigint;
|
|
1000
|
+
readonly count: bigint;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter", {
|
|
1004
|
+
fourState: true,
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
sim.addClock("clk", { period: 10 });
|
|
1008
|
+
|
|
1009
|
+
// Reset (default async_low: rst=0 is active)
|
|
1010
|
+
sim.dut.rst = 0n;
|
|
1011
|
+
sim.runUntil(20);
|
|
1012
|
+
sim.dut.rst = 1n;
|
|
1013
|
+
sim.dut.en = 1n;
|
|
1014
|
+
|
|
1015
|
+
sim.runUntil(100);
|
|
1016
|
+
|
|
1017
|
+
const count = sim.dut.count;
|
|
1018
|
+
expect(count).toBeGreaterThan(0n);
|
|
1019
|
+
expect(sim.time()).toBe(100);
|
|
1020
|
+
|
|
1021
|
+
sim.dispose();
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
test("4-state combinational in time-based simulation", () => {
|
|
1025
|
+
interface Ports {
|
|
1026
|
+
a: bigint;
|
|
1027
|
+
b: bigint;
|
|
1028
|
+
readonly y: bigint;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
const sim = Simulation.fromSource<Ports>(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
1032
|
+
fourState: true,
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1035
|
+
// No clock needed for pure combinational — just set values and read
|
|
1036
|
+
sim.dut.a = 100n;
|
|
1037
|
+
sim.dut.b = 55n;
|
|
1038
|
+
// runUntil(0) to force eval
|
|
1039
|
+
sim.runUntil(0);
|
|
1040
|
+
expect(sim.dut.y).toBe(155n);
|
|
1041
|
+
|
|
1042
|
+
sim.dispose();
|
|
1043
|
+
});
|
|
1008
1044
|
});
|
|
1009
1045
|
|
|
1010
1046
|
// ---------------------------------------------------------------------------
|
|
@@ -1012,225 +1048,225 @@ describe("E2E: 4-state Simulation (time-based)", () => {
|
|
|
1012
1048
|
// ---------------------------------------------------------------------------
|
|
1013
1049
|
|
|
1014
1050
|
describe("E2E: Simulation testbench helpers", () => {
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1051
|
+
test("waitForCycles: advances correct number of clock cycles", () => {
|
|
1052
|
+
interface CounterPorts {
|
|
1053
|
+
rst: bigint;
|
|
1054
|
+
en: bigint;
|
|
1055
|
+
readonly count: bigint;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
1059
|
+
sim.addClock("clk", { period: 10 });
|
|
1060
|
+
|
|
1061
|
+
// Reset (default async_low: rst=0 is active)
|
|
1062
|
+
sim.dut.rst = 0n;
|
|
1063
|
+
sim.runUntil(20);
|
|
1064
|
+
sim.dut.rst = 1n;
|
|
1065
|
+
sim.dut.en = 1n;
|
|
1066
|
+
|
|
1067
|
+
const beforeTime = sim.time();
|
|
1068
|
+
const afterTime = sim.waitForCycles("clk", 5);
|
|
1069
|
+
|
|
1070
|
+
expect(afterTime).toBeGreaterThan(beforeTime);
|
|
1071
|
+
// Each cycle = 2 steps with period 10, so 5 cycles ≈ 50 time units
|
|
1072
|
+
expect(afterTime - beforeTime).toBe(50);
|
|
1073
|
+
|
|
1074
|
+
sim.dispose();
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
test("waitUntil: waits for condition to be met", () => {
|
|
1078
|
+
interface CounterPorts {
|
|
1079
|
+
rst: bigint;
|
|
1080
|
+
en: bigint;
|
|
1081
|
+
readonly count: bigint;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
1085
|
+
sim.addClock("clk", { period: 10 });
|
|
1086
|
+
|
|
1087
|
+
// Reset (default async_low: rst=0 is active)
|
|
1088
|
+
sim.dut.rst = 0n;
|
|
1089
|
+
sim.runUntil(20);
|
|
1090
|
+
sim.dut.rst = 1n;
|
|
1091
|
+
sim.dut.en = 1n;
|
|
1092
|
+
|
|
1093
|
+
const t = sim.waitUntil(() => sim.dut.count >= 3n);
|
|
1094
|
+
expect(sim.dut.count).toBeGreaterThanOrEqual(3n);
|
|
1095
|
+
expect(t).toBeGreaterThan(20);
|
|
1096
|
+
|
|
1097
|
+
sim.dispose();
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
test("waitUntil: throws SimulationTimeoutError on timeout", () => {
|
|
1101
|
+
interface CounterPorts {
|
|
1102
|
+
rst: bigint;
|
|
1103
|
+
en: bigint;
|
|
1104
|
+
readonly count: bigint;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
1108
|
+
sim.addClock("clk", { period: 10 });
|
|
1109
|
+
|
|
1110
|
+
// Reset (default async_low: rst=0 is active)
|
|
1111
|
+
sim.dut.rst = 0n;
|
|
1112
|
+
sim.runUntil(20);
|
|
1113
|
+
sim.dut.rst = 1n;
|
|
1114
|
+
sim.dut.en = 0n; // disabled — count won't increase
|
|
1115
|
+
|
|
1116
|
+
expect(() =>
|
|
1117
|
+
sim.waitUntil(() => sim.dut.count >= 100n, { maxSteps: 20 }),
|
|
1118
|
+
).toThrow(SimulationTimeoutError);
|
|
1119
|
+
|
|
1120
|
+
sim.dispose();
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
test("reset: asserts and releases reset on counter", () => {
|
|
1124
|
+
interface CounterPorts {
|
|
1125
|
+
rst: bigint;
|
|
1126
|
+
en: bigint;
|
|
1127
|
+
readonly count: bigint;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
1131
|
+
sim.addClock("clk", { period: 10 });
|
|
1132
|
+
|
|
1133
|
+
// Count up a bit (default async_low: rst=0 is active)
|
|
1134
|
+
sim.dut.rst = 0n;
|
|
1135
|
+
sim.runUntil(20);
|
|
1136
|
+
sim.dut.rst = 1n;
|
|
1137
|
+
sim.dut.en = 1n;
|
|
1138
|
+
sim.runUntil(100);
|
|
1139
|
+
expect(sim.dut.count).toBeGreaterThan(0n);
|
|
1140
|
+
|
|
1141
|
+
// Reset using the helper
|
|
1142
|
+
sim.reset("rst");
|
|
1143
|
+
expect(sim.dut.count).toBe(0n);
|
|
1144
|
+
|
|
1145
|
+
sim.dispose();
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
test("reset: explicit async_low resetType activates with 0", () => {
|
|
1149
|
+
interface CounterPorts {
|
|
1150
|
+
rst: bigint;
|
|
1151
|
+
en: bigint;
|
|
1152
|
+
readonly count: bigint;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter", {
|
|
1156
|
+
resetType: "async_low",
|
|
1157
|
+
});
|
|
1158
|
+
sim.addClock("clk", { period: 10 });
|
|
1159
|
+
|
|
1160
|
+
// With async_low, rst=0 is active, rst=1 is inactive
|
|
1161
|
+
sim.reset("rst");
|
|
1162
|
+
|
|
1163
|
+
sim.dut.en = 1n;
|
|
1164
|
+
sim.runUntil(100);
|
|
1165
|
+
expect(sim.dut.count).toBeGreaterThan(0n);
|
|
1166
|
+
|
|
1167
|
+
// Reset again using helper, verify it resets the counter
|
|
1168
|
+
sim.reset("rst");
|
|
1169
|
+
expect(sim.dut.count).toBe(0n);
|
|
1170
|
+
|
|
1171
|
+
sim.dispose();
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
test("reset: explicit async_high resetType activates with 1", () => {
|
|
1175
|
+
interface CounterPorts {
|
|
1176
|
+
rst: bigint;
|
|
1177
|
+
en: bigint;
|
|
1178
|
+
readonly count: bigint;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter", {
|
|
1182
|
+
resetType: "async_high",
|
|
1183
|
+
});
|
|
1184
|
+
sim.addClock("clk", { period: 10 });
|
|
1185
|
+
|
|
1186
|
+
// With async_high, rst=1 is active, rst=0 is inactive
|
|
1187
|
+
sim.reset("rst");
|
|
1188
|
+
|
|
1189
|
+
sim.dut.en = 1n;
|
|
1190
|
+
sim.runUntil(100);
|
|
1191
|
+
expect(sim.dut.count).toBeGreaterThan(0n);
|
|
1192
|
+
|
|
1193
|
+
// Reset again
|
|
1194
|
+
sim.reset("rst");
|
|
1195
|
+
expect(sim.dut.count).toBe(0n);
|
|
1196
|
+
|
|
1197
|
+
sim.dispose();
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
test("Simulator.fromSource: resetType option works", () => {
|
|
1201
|
+
interface CounterPorts {
|
|
1202
|
+
rst: bigint;
|
|
1203
|
+
en: bigint;
|
|
1204
|
+
readonly count: bigint;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
const sim = Simulator.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter", {
|
|
1208
|
+
resetType: "async_high",
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
// With async_high, assert reset with 1
|
|
1212
|
+
sim.dut.rst = 1n;
|
|
1213
|
+
sim.tick();
|
|
1214
|
+
sim.dut.rst = 0n;
|
|
1215
|
+
sim.tick();
|
|
1216
|
+
expect(sim.dut.count).toBe(0n);
|
|
1217
|
+
|
|
1218
|
+
// Count up
|
|
1219
|
+
sim.dut.en = 1n;
|
|
1220
|
+
sim.tick();
|
|
1221
|
+
expect(sim.dut.count).toBe(1n);
|
|
1222
|
+
|
|
1223
|
+
sim.dispose();
|
|
1224
|
+
});
|
|
1225
|
+
|
|
1226
|
+
test("runUntil with maxSteps: succeeds within budget", () => {
|
|
1227
|
+
interface CounterPorts {
|
|
1228
|
+
rst: bigint;
|
|
1229
|
+
en: bigint;
|
|
1230
|
+
readonly count: bigint;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
1234
|
+
sim.addClock("clk", { period: 10 });
|
|
1235
|
+
|
|
1236
|
+
// Reset (default async_low: rst=0 is active)
|
|
1237
|
+
sim.dut.rst = 0n;
|
|
1238
|
+
sim.runUntil(20);
|
|
1239
|
+
sim.dut.rst = 1n;
|
|
1240
|
+
sim.dut.en = 1n;
|
|
1241
|
+
|
|
1242
|
+
// 100 time units with period 10 = 10 events, should fit in 100 steps
|
|
1243
|
+
sim.runUntil(120, { maxSteps: 100 });
|
|
1244
|
+
expect(sim.time()).toBe(120);
|
|
1245
|
+
|
|
1246
|
+
sim.dispose();
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1249
|
+
test("runUntil with maxSteps: throws on exceeded budget", () => {
|
|
1250
|
+
interface CounterPorts {
|
|
1251
|
+
rst: bigint;
|
|
1252
|
+
en: bigint;
|
|
1253
|
+
readonly count: bigint;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
1257
|
+
sim.addClock("clk", { period: 10 });
|
|
1258
|
+
|
|
1259
|
+
// Release reset so counter can count (default async_low: rst=1 is inactive)
|
|
1260
|
+
sim.dut.rst = 1n;
|
|
1261
|
+
sim.dut.en = 1n;
|
|
1262
|
+
|
|
1263
|
+
// Very small budget for a long run
|
|
1264
|
+
expect(() => sim.runUntil(100000, { maxSteps: 5 })).toThrow(
|
|
1265
|
+
SimulationTimeoutError,
|
|
1266
|
+
);
|
|
1267
|
+
|
|
1268
|
+
sim.dispose();
|
|
1269
|
+
});
|
|
1234
1270
|
});
|
|
1235
1271
|
|
|
1236
1272
|
// ---------------------------------------------------------------------------
|
|
@@ -1238,89 +1274,89 @@ describe("E2E: Simulation testbench helpers", () => {
|
|
|
1238
1274
|
// ---------------------------------------------------------------------------
|
|
1239
1275
|
|
|
1240
1276
|
describe("E2E: fourState() method", () => {
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1277
|
+
test("Simulator.fourState: reads 4-state value and mask", () => {
|
|
1278
|
+
interface Ports {
|
|
1279
|
+
a: bigint;
|
|
1280
|
+
b: bigint;
|
|
1281
|
+
readonly y: bigint;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
const sim = Simulator.fromSource<Ports>(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
1285
|
+
fourState: true,
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
sim.dut.a = 100n;
|
|
1289
|
+
sim.dut.b = 55n;
|
|
1290
|
+
// Trigger evalComb via output read (Adder4S is purely combinational)
|
|
1291
|
+
expect(sim.dut.y).toBe(155n);
|
|
1292
|
+
|
|
1293
|
+
const fs = sim.fourState("y");
|
|
1294
|
+
expect(fs.__fourState).toBe(true);
|
|
1295
|
+
expect(fs.value).toBe(155n);
|
|
1296
|
+
expect(fs.mask).toBe(0n);
|
|
1297
|
+
|
|
1298
|
+
sim.dispose();
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
test("Simulator.fourState: reads X mask when input is X", () => {
|
|
1302
|
+
interface Ports {
|
|
1303
|
+
a: bigint;
|
|
1304
|
+
b: bigint;
|
|
1305
|
+
readonly y: bigint;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
const sim = Simulator.fromSource<Ports>(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
1309
|
+
fourState: true,
|
|
1310
|
+
});
|
|
1311
|
+
|
|
1312
|
+
(sim.dut as any).a = X;
|
|
1313
|
+
sim.dut.b = 10n;
|
|
1314
|
+
// Trigger evalComb via output read
|
|
1315
|
+
sim.dut.y;
|
|
1316
|
+
|
|
1317
|
+
const fs = sim.fourState("y");
|
|
1318
|
+
expect(fs.mask).toBe(0xffn); // all X from arithmetic propagation
|
|
1319
|
+
|
|
1320
|
+
sim.dispose();
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
test("Simulation.fourState: reads 4-state value", () => {
|
|
1324
|
+
interface Ports {
|
|
1325
|
+
a: bigint;
|
|
1326
|
+
b: bigint;
|
|
1327
|
+
readonly y: bigint;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
const sim = Simulation.fromSource<Ports>(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
1331
|
+
fourState: true,
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
sim.dut.a = 50n;
|
|
1335
|
+
sim.dut.b = 25n;
|
|
1336
|
+
sim.runUntil(0);
|
|
1337
|
+
|
|
1338
|
+
const fs = sim.fourState("y");
|
|
1339
|
+
expect(fs.value).toBe(75n);
|
|
1340
|
+
expect(fs.mask).toBe(0n);
|
|
1341
|
+
|
|
1342
|
+
sim.dispose();
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
test("fourState: throws for unknown port", () => {
|
|
1346
|
+
interface Ports {
|
|
1347
|
+
a: bigint;
|
|
1348
|
+
b: bigint;
|
|
1349
|
+
readonly y: bigint;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
const sim = Simulator.fromSource<Ports>(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
1353
|
+
fourState: true,
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
expect(() => sim.fourState("nonexistent")).toThrow("Unknown port");
|
|
1357
|
+
|
|
1358
|
+
sim.dispose();
|
|
1359
|
+
});
|
|
1324
1360
|
});
|
|
1325
1361
|
|
|
1326
1362
|
// ---------------------------------------------------------------------------
|
|
@@ -1328,50 +1364,50 @@ describe("E2E: fourState() method", () => {
|
|
|
1328
1364
|
// ---------------------------------------------------------------------------
|
|
1329
1365
|
|
|
1330
1366
|
describe("E2E: optimize flag", () => {
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1367
|
+
test("Simulator.fromSource with optimize: true", () => {
|
|
1368
|
+
interface AdderPorts {
|
|
1369
|
+
rst: bigint;
|
|
1370
|
+
a: bigint;
|
|
1371
|
+
b: bigint;
|
|
1372
|
+
readonly sum: bigint;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
const sim = Simulator.fromSource<AdderPorts>(ADDER_SOURCE, "Adder", {
|
|
1376
|
+
optimize: true,
|
|
1377
|
+
});
|
|
1378
|
+
|
|
1379
|
+
sim.dut.a = 100n;
|
|
1380
|
+
sim.dut.b = 200n;
|
|
1381
|
+
sim.tick();
|
|
1382
|
+
expect(sim.dut.sum).toBe(300n);
|
|
1383
|
+
|
|
1384
|
+
sim.dispose();
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1387
|
+
test("Simulation.fromSource with optimize: true", () => {
|
|
1388
|
+
interface CounterPorts {
|
|
1389
|
+
rst: bigint;
|
|
1390
|
+
en: bigint;
|
|
1391
|
+
readonly count: bigint;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter", {
|
|
1395
|
+
optimize: true,
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
sim.addClock("clk", { period: 10 });
|
|
1399
|
+
|
|
1400
|
+
// Reset (default async_low: rst=0 is active)
|
|
1401
|
+
sim.dut.rst = 0n;
|
|
1402
|
+
sim.runUntil(20);
|
|
1403
|
+
sim.dut.rst = 1n;
|
|
1404
|
+
sim.dut.en = 1n;
|
|
1405
|
+
sim.runUntil(100);
|
|
1406
|
+
|
|
1407
|
+
expect(sim.dut.count).toBeGreaterThan(0n);
|
|
1408
|
+
|
|
1409
|
+
sim.dispose();
|
|
1410
|
+
});
|
|
1375
1411
|
});
|
|
1376
1412
|
|
|
1377
1413
|
// ---------------------------------------------------------------------------
|
|
@@ -1379,32 +1415,32 @@ describe("E2E: optimize flag", () => {
|
|
|
1379
1415
|
// ---------------------------------------------------------------------------
|
|
1380
1416
|
|
|
1381
1417
|
describe("parseSignalPath", () => {
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1418
|
+
test("simple variable path", () => {
|
|
1419
|
+
const result = parseSignalPath("v");
|
|
1420
|
+
expect(result.instancePath).toEqual([]);
|
|
1421
|
+
expect(result.varPath).toEqual(["v"]);
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
test("instance:variable split", () => {
|
|
1425
|
+
const result = parseSignalPath("p2:i");
|
|
1426
|
+
expect(result.instancePath).toEqual([{ name: "p2", index: 0 }]);
|
|
1427
|
+
expect(result.varPath).toEqual(["i"]);
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
test("nested instance with array index", () => {
|
|
1431
|
+
const result = parseSignalPath("a.b[3]:x.y");
|
|
1432
|
+
expect(result.instancePath).toEqual([
|
|
1433
|
+
{ name: "a", index: 0 },
|
|
1434
|
+
{ name: "b", index: 3 },
|
|
1435
|
+
]);
|
|
1436
|
+
expect(result.varPath).toEqual(["x", "y"]);
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
test("dotted variable path without instance", () => {
|
|
1440
|
+
const result = parseSignalPath("foo.bar.baz");
|
|
1441
|
+
expect(result.instancePath).toEqual([]);
|
|
1442
|
+
expect(result.varPath).toEqual(["foo", "bar", "baz"]);
|
|
1443
|
+
});
|
|
1408
1444
|
});
|
|
1409
1445
|
|
|
1410
1446
|
// ---------------------------------------------------------------------------
|
|
@@ -1486,179 +1522,195 @@ module ParamChild #(
|
|
|
1486
1522
|
`;
|
|
1487
1523
|
|
|
1488
1524
|
describe("E2E: child instance DUT access", () => {
|
|
1489
|
-
|
|
1490
|
-
|
|
1525
|
+
test("Simulator: read child instance ports via dut.u_sub", () => {
|
|
1526
|
+
const sim = Simulator.fromSource(HIERARCHY_SOURCE, "Top");
|
|
1491
1527
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1528
|
+
sim.dut.top_in = 0xabn;
|
|
1529
|
+
sim.tick();
|
|
1530
|
+
expect(sim.dut.top_out).toBe(0xabn);
|
|
1495
1531
|
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1532
|
+
// Read the same value through the child instance accessor
|
|
1533
|
+
expect((sim.dut as any).u_sub.o_data).toBe(0xabn);
|
|
1534
|
+
expect((sim.dut as any).u_sub.i_data).toBe(0xabn);
|
|
1499
1535
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1536
|
+
// Change top-level input and verify child reflects it
|
|
1537
|
+
sim.dut.top_in = 0x42n;
|
|
1538
|
+
sim.tick();
|
|
1539
|
+
expect((sim.dut as any).u_sub.i_data).toBe(0x42n);
|
|
1540
|
+
expect((sim.dut as any).u_sub.o_data).toBe(0x42n);
|
|
1505
1541
|
|
|
1506
|
-
|
|
1507
|
-
|
|
1542
|
+
sim.dispose();
|
|
1543
|
+
});
|
|
1508
1544
|
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1545
|
+
test("Simulation: read child instance ports via dut.u_sub", () => {
|
|
1546
|
+
const sim = Simulation.fromSource(HIERARCHY_SOURCE, "Top");
|
|
1547
|
+
sim.addClock("clk", { period: 10 });
|
|
1512
1548
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1549
|
+
sim.dut.rst = 0n;
|
|
1550
|
+
sim.runUntil(20);
|
|
1551
|
+
sim.dut.rst = 1n;
|
|
1516
1552
|
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1553
|
+
sim.dut.top_in = 0x55n;
|
|
1554
|
+
sim.runUntil(40);
|
|
1555
|
+
expect((sim.dut as any).u_sub.o_data).toBe(0x55n);
|
|
1556
|
+
expect((sim.dut as any).u_sub.i_data).toBe(0x55n);
|
|
1521
1557
|
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1558
|
+
sim.dut.top_in = 0xffn;
|
|
1559
|
+
sim.runUntil(60);
|
|
1560
|
+
expect((sim.dut as any).u_sub.o_data).toBe(0xffn);
|
|
1525
1561
|
|
|
1526
|
-
|
|
1527
|
-
|
|
1562
|
+
sim.dispose();
|
|
1563
|
+
});
|
|
1528
1564
|
});
|
|
1529
1565
|
|
|
1530
1566
|
describe("E2E: parameter override — DUT correctness", () => {
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1567
|
+
test("scalar width override: WIDTH 8→16 via fromSource", () => {
|
|
1568
|
+
interface Ports {
|
|
1569
|
+
a: bigint;
|
|
1570
|
+
readonly b: bigint;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
// Default WIDTH=8 (purely combinational — read output to trigger evalComb)
|
|
1574
|
+
const sim8 = Simulator.fromSource<Ports>(PARAM_WIDTH_SOURCE, "ParamWidth");
|
|
1575
|
+
sim8.dut.a = 0xabn;
|
|
1576
|
+
expect(sim8.dut.b).toBe(0xabn);
|
|
1577
|
+
// Writing a 16-bit value to an 8-bit port should truncate
|
|
1578
|
+
sim8.dut.a = 0xabcdn;
|
|
1579
|
+
expect(sim8.dut.b).toBe(0xcdn); // truncated to 8 bits
|
|
1580
|
+
sim8.dispose();
|
|
1581
|
+
|
|
1582
|
+
// Override WIDTH=16 — DUT should handle 16-bit values
|
|
1583
|
+
const sim16 = Simulator.fromSource<Ports>(
|
|
1584
|
+
PARAM_WIDTH_SOURCE,
|
|
1585
|
+
"ParamWidth",
|
|
1586
|
+
{
|
|
1587
|
+
parameters: [{ name: "WIDTH", value: 16 }],
|
|
1588
|
+
},
|
|
1589
|
+
);
|
|
1590
|
+
sim16.dut.a = 0xabcdn;
|
|
1591
|
+
expect(sim16.dut.b).toBe(0xabcdn); // full 16-bit value preserved
|
|
1592
|
+
sim16.dispose();
|
|
1593
|
+
});
|
|
1594
|
+
|
|
1595
|
+
test("scalar width override: WIDTH 8→32 via fromSource", () => {
|
|
1596
|
+
interface Ports {
|
|
1597
|
+
a: bigint;
|
|
1598
|
+
readonly b: bigint;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
const sim = Simulator.fromSource<Ports>(PARAM_WIDTH_SOURCE, "ParamWidth", {
|
|
1602
|
+
parameters: [{ name: "WIDTH", value: 32 }],
|
|
1603
|
+
});
|
|
1604
|
+
sim.dut.a = 0xdead_beefn;
|
|
1605
|
+
expect(sim.dut.b).toBe(0xdead_beefn);
|
|
1606
|
+
sim.dispose();
|
|
1607
|
+
});
|
|
1608
|
+
|
|
1609
|
+
test.skip("param value reflected in logic via fromSource", () => {
|
|
1610
|
+
// blocked by upstream Veryl IR bug
|
|
1611
|
+
interface Ports {
|
|
1612
|
+
a: bigint;
|
|
1613
|
+
readonly b: bigint;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// Default OFFSET=10
|
|
1617
|
+
const sim10 = Simulator.fromSource<Ports>(
|
|
1618
|
+
PARAM_OFFSET_SOURCE,
|
|
1619
|
+
"ParamOffset",
|
|
1620
|
+
);
|
|
1621
|
+
sim10.dut.a = 5n;
|
|
1622
|
+
expect(sim10.dut.b).toBe(15n);
|
|
1623
|
+
sim10.dispose();
|
|
1624
|
+
|
|
1625
|
+
// Override OFFSET=100
|
|
1626
|
+
const sim100 = Simulator.fromSource<Ports>(
|
|
1627
|
+
PARAM_OFFSET_SOURCE,
|
|
1628
|
+
"ParamOffset",
|
|
1629
|
+
{
|
|
1630
|
+
parameters: [{ name: "OFFSET", value: 100 }],
|
|
1631
|
+
},
|
|
1632
|
+
);
|
|
1633
|
+
sim100.dut.a = 5n;
|
|
1634
|
+
expect(sim100.dut.b).toBe(105n);
|
|
1635
|
+
sim100.dispose();
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
test("child module param propagation via fromSource", () => {
|
|
1639
|
+
interface Ports {
|
|
1640
|
+
a: bigint;
|
|
1641
|
+
readonly b: bigint;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
// Default WIDTH=8
|
|
1645
|
+
const sim8 = Simulator.fromSource<Ports>(PARAM_CHILD_SOURCE, "ParamChild");
|
|
1646
|
+
sim8.dut.a = 0xabn;
|
|
1647
|
+
expect(sim8.dut.b).toBe(0xabn);
|
|
1648
|
+
sim8.dispose();
|
|
1649
|
+
|
|
1650
|
+
// Override WIDTH=16 — child also gets 16-bit ports
|
|
1651
|
+
const sim16 = Simulator.fromSource<Ports>(
|
|
1652
|
+
PARAM_CHILD_SOURCE,
|
|
1653
|
+
"ParamChild",
|
|
1654
|
+
{
|
|
1655
|
+
parameters: [{ name: "WIDTH", value: 16 }],
|
|
1656
|
+
},
|
|
1657
|
+
);
|
|
1658
|
+
sim16.dut.a = 0xabcdn;
|
|
1659
|
+
expect(sim16.dut.b).toBe(0xabcdn);
|
|
1660
|
+
sim16.dispose();
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
test("Simulator.create() with stale ModuleDefinition + param override", () => {
|
|
1664
|
+
// This is the most dangerous case: ModuleDefinition has width=8 (stale),
|
|
1665
|
+
// but we override WIDTH=16. The DUT must use runtime-derived ports
|
|
1666
|
+
// from hierarchy, not the stale module.ports.
|
|
1667
|
+
interface Ports {
|
|
1668
|
+
a: bigint;
|
|
1669
|
+
readonly b: bigint;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
const addon = loadNativeAddon();
|
|
1673
|
+
const nativeCreate = createSimulatorBridge(addon);
|
|
1674
|
+
|
|
1675
|
+
const sim = Simulator.create<Ports>(
|
|
1676
|
+
{
|
|
1677
|
+
__celox_module: true,
|
|
1678
|
+
name: "ParamWidth",
|
|
1679
|
+
source: PARAM_WIDTH_SOURCE,
|
|
1680
|
+
ports: {
|
|
1681
|
+
// STALE: these say width=8, but we'll override to 16
|
|
1682
|
+
a: { direction: "input", type: "logic", width: 8 },
|
|
1683
|
+
b: { direction: "output", type: "logic", width: 8 },
|
|
1684
|
+
},
|
|
1685
|
+
events: [],
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
__nativeCreate: nativeCreate,
|
|
1689
|
+
parameters: [{ name: "WIDTH", value: 16 }],
|
|
1690
|
+
},
|
|
1691
|
+
);
|
|
1692
|
+
|
|
1693
|
+
// If DUT used stale ports (width=8), this would truncate or corrupt
|
|
1694
|
+
sim.dut.a = 0xabcdn;
|
|
1695
|
+
expect(sim.dut.b).toBe(0xabcdn); // full 16-bit value preserved
|
|
1696
|
+
sim.dispose();
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1699
|
+
test("Simulation.fromSource with param override", () => {
|
|
1700
|
+
interface Ports {
|
|
1701
|
+
a: bigint;
|
|
1702
|
+
readonly b: bigint;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
const sim = Simulation.fromSource<Ports>(PARAM_WIDTH_SOURCE, "ParamWidth", {
|
|
1706
|
+
parameters: [{ name: "WIDTH", value: 16 }],
|
|
1707
|
+
});
|
|
1708
|
+
|
|
1709
|
+
sim.dut.a = 0xfedcn;
|
|
1710
|
+
sim.runUntil(0);
|
|
1711
|
+
expect(sim.dut.b).toBe(0xfedcn);
|
|
1712
|
+
sim.dispose();
|
|
1713
|
+
});
|
|
1662
1714
|
});
|
|
1663
1715
|
|
|
1664
1716
|
// ---------------------------------------------------------------------------
|
|
@@ -1666,65 +1718,77 @@ describe("E2E: parameter override — DUT correctness", () => {
|
|
|
1666
1718
|
// ---------------------------------------------------------------------------
|
|
1667
1719
|
|
|
1668
1720
|
describe("E2E: celox.toml test sources", () => {
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1721
|
+
test("fromProject loads test-only module declared in celox.toml", () => {
|
|
1722
|
+
interface RegPorts {
|
|
1723
|
+
rst: bigint;
|
|
1724
|
+
d: bigint;
|
|
1725
|
+
readonly q: bigint;
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
// `Reg` lives in test_veryl/ — not in Veryl.toml sources — but celox.toml
|
|
1729
|
+
// declares test_veryl/ as an additional test source directory.
|
|
1730
|
+
const sim = Simulator.fromProject<RegPorts>(CELOX_TOML_PROJECT, "Reg");
|
|
1731
|
+
|
|
1732
|
+
// Deassert async_low reset (rst=0 is active; rst=1 is normal operation)
|
|
1733
|
+
sim.dut.rst = 1n;
|
|
1734
|
+
sim.dut.d = 0xabn;
|
|
1735
|
+
sim.tick();
|
|
1736
|
+
expect(sim.dut.q).toBe(0xabn);
|
|
1737
|
+
|
|
1738
|
+
sim.dut.d = 0xcdn;
|
|
1739
|
+
expect(sim.dut.q).toBe(0xabn); // not yet clocked
|
|
1740
|
+
sim.tick();
|
|
1741
|
+
expect(sim.dut.q).toBe(0xcdn);
|
|
1742
|
+
|
|
1743
|
+
sim.dispose();
|
|
1744
|
+
});
|
|
1745
|
+
|
|
1746
|
+
test("genTs includes test-only module declared in celox.toml", () => {
|
|
1747
|
+
const addon = loadNativeAddon();
|
|
1748
|
+
const json = addon.genTs(CELOX_TOML_PROJECT);
|
|
1749
|
+
const output = JSON.parse(json) as {
|
|
1750
|
+
modules: Array<{ moduleName: string }>;
|
|
1751
|
+
};
|
|
1752
|
+
const names = output.modules.map((m) => m.moduleName);
|
|
1753
|
+
// Both the regular source (Adder) and the test-only source (Reg) must appear
|
|
1754
|
+
expect(names).toContain("Adder");
|
|
1755
|
+
expect(names).toContain("Reg");
|
|
1756
|
+
});
|
|
1757
|
+
|
|
1758
|
+
test("[simulation] max_steps from celox.toml is used as waitUntil default", () => {
|
|
1759
|
+
// celox.toml sets [simulation] max_steps = 20; waitUntil should honour it
|
|
1760
|
+
// when no per-call maxSteps is provided.
|
|
1761
|
+
const sim = Simulation.fromProject(CELOX_TOML_PROJECT, "Reg");
|
|
1762
|
+
sim.addClock("clk", { period: 10 });
|
|
1763
|
+
expect(() => sim.waitUntil(() => false)).toThrow(SimulationTimeoutError);
|
|
1764
|
+
const err = (() => {
|
|
1765
|
+
try {
|
|
1766
|
+
sim.waitUntil(() => false);
|
|
1767
|
+
} catch (e) {
|
|
1768
|
+
return e;
|
|
1769
|
+
}
|
|
1770
|
+
})() as SimulationTimeoutError;
|
|
1771
|
+
expect(err.steps).toBe(20);
|
|
1772
|
+
sim.dispose();
|
|
1773
|
+
});
|
|
1774
|
+
|
|
1775
|
+
test("[simulation] max_steps per-call override takes precedence over celox.toml", () => {
|
|
1776
|
+
const sim = Simulation.fromProject(CELOX_TOML_PROJECT, "Reg");
|
|
1777
|
+
sim.addClock("clk", { period: 10 });
|
|
1778
|
+
// Per-call maxSteps=5 is lower than the toml default of 20
|
|
1779
|
+
expect(() => sim.waitUntil(() => false, { maxSteps: 5 })).toThrow(
|
|
1780
|
+
SimulationTimeoutError,
|
|
1781
|
+
);
|
|
1782
|
+
const err = (() => {
|
|
1783
|
+
try {
|
|
1784
|
+
sim.waitUntil(() => false, { maxSteps: 5 });
|
|
1785
|
+
} catch (e) {
|
|
1786
|
+
return e;
|
|
1787
|
+
}
|
|
1788
|
+
})() as SimulationTimeoutError;
|
|
1789
|
+
expect(err.steps).toBe(5);
|
|
1790
|
+
sim.dispose();
|
|
1791
|
+
});
|
|
1728
1792
|
});
|
|
1729
1793
|
|
|
1730
1794
|
// ---------------------------------------------------------------------------
|
|
@@ -1802,29 +1866,29 @@ module Top (
|
|
|
1802
1866
|
`;
|
|
1803
1867
|
|
|
1804
1868
|
describe("E2E: interface port DUT accessor", () => {
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1869
|
+
test("top-level interface port members accessible as nested dut properties", () => {
|
|
1870
|
+
interface TopPorts {
|
|
1871
|
+
sig: { data: bigint; valid: bigint };
|
|
1872
|
+
readonly out: bigint;
|
|
1873
|
+
}
|
|
1810
1874
|
|
|
1811
|
-
|
|
1875
|
+
const sim = Simulator.fromSource<TopPorts>(INTERFACE_PORT_SOURCE, "Top");
|
|
1812
1876
|
|
|
1813
|
-
|
|
1814
|
-
|
|
1877
|
+
sim.dut.sig.data = 0x42n;
|
|
1878
|
+
expect(sim.dut.out).toBe(0x42n);
|
|
1815
1879
|
|
|
1816
|
-
|
|
1817
|
-
|
|
1880
|
+
sim.dut.sig.data = 0xffn;
|
|
1881
|
+
expect(sim.dut.out).toBe(0xffn);
|
|
1818
1882
|
|
|
1819
|
-
|
|
1820
|
-
|
|
1883
|
+
sim.dut.sig.data = 0x00n;
|
|
1884
|
+
expect(sim.dut.out).toBe(0x00n);
|
|
1821
1885
|
|
|
1822
|
-
|
|
1823
|
-
|
|
1886
|
+
sim.dispose();
|
|
1887
|
+
});
|
|
1824
1888
|
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1889
|
+
test("writing to interface output member throws", () => {
|
|
1890
|
+
// bus.data / bus.valid are outputs of Top — writing should throw.
|
|
1891
|
+
const PRODUCER_ONLY = `
|
|
1828
1892
|
interface Bus {
|
|
1829
1893
|
var data: logic<8>;
|
|
1830
1894
|
var valid: logic;
|
|
@@ -1841,52 +1905,112 @@ module Top (
|
|
|
1841
1905
|
assign bus.valid = 1'b1;
|
|
1842
1906
|
}
|
|
1843
1907
|
`;
|
|
1844
|
-
|
|
1908
|
+
const sim = Simulator.fromSource(PRODUCER_ONLY, "Top");
|
|
1845
1909
|
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1910
|
+
expect(() => {
|
|
1911
|
+
(sim.dut as any).bus.data = 0n;
|
|
1912
|
+
}).toThrow("Cannot write to output port 'data'");
|
|
1849
1913
|
|
|
1850
|
-
|
|
1851
|
-
|
|
1914
|
+
sim.dispose();
|
|
1915
|
+
});
|
|
1852
1916
|
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1917
|
+
test("interface port propagates through sub-module instances", () => {
|
|
1918
|
+
interface TopPorts {
|
|
1919
|
+
val: bigint;
|
|
1920
|
+
readonly out: bigint;
|
|
1921
|
+
readonly got: bigint;
|
|
1922
|
+
}
|
|
1859
1923
|
|
|
1860
|
-
|
|
1924
|
+
const sim = Simulator.fromSource<TopPorts>(
|
|
1925
|
+
INTERFACE_HIERARCHY_SOURCE,
|
|
1926
|
+
"Top",
|
|
1927
|
+
);
|
|
1861
1928
|
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1929
|
+
sim.dut.val = 0xabn;
|
|
1930
|
+
expect(sim.dut.out).toBe(0xabn);
|
|
1931
|
+
expect(sim.dut.got).toBe(1n);
|
|
1865
1932
|
|
|
1866
|
-
|
|
1867
|
-
|
|
1933
|
+
sim.dut.val = 0x00n;
|
|
1934
|
+
expect(sim.dut.out).toBe(0x00n);
|
|
1868
1935
|
|
|
1869
|
-
|
|
1870
|
-
|
|
1936
|
+
sim.dispose();
|
|
1937
|
+
});
|
|
1871
1938
|
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1939
|
+
test("multiple interface port members are independently accessible", () => {
|
|
1940
|
+
interface TopPorts {
|
|
1941
|
+
sig: { data: bigint; valid: bigint };
|
|
1942
|
+
readonly out: bigint;
|
|
1943
|
+
}
|
|
1877
1944
|
|
|
1878
|
-
|
|
1945
|
+
const sim = Simulator.fromSource<TopPorts>(INTERFACE_PORT_SOURCE, "Top");
|
|
1879
1946
|
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1947
|
+
sim.dut.sig.data = 0x77n;
|
|
1948
|
+
sim.dut.sig.valid = 1n;
|
|
1949
|
+
expect(sim.dut.out).toBe(0x77n);
|
|
1883
1950
|
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1951
|
+
sim.dut.sig.data = 0x33n;
|
|
1952
|
+
sim.dut.sig.valid = 0n;
|
|
1953
|
+
expect(sim.dut.out).toBe(0x33n);
|
|
1887
1954
|
|
|
1888
|
-
|
|
1889
|
-
|
|
1955
|
+
sim.dispose();
|
|
1956
|
+
});
|
|
1957
|
+
});
|
|
1958
|
+
|
|
1959
|
+
// ---------------------------------------------------------------------------
|
|
1960
|
+
// Interface array port (modport [N])
|
|
1961
|
+
// ---------------------------------------------------------------------------
|
|
1962
|
+
|
|
1963
|
+
const INTERFACE_ARRAY_SOURCE = `
|
|
1964
|
+
interface Bus {
|
|
1965
|
+
var data: logic<8>;
|
|
1966
|
+
var valid: logic;
|
|
1967
|
+
modport consumer {
|
|
1968
|
+
data: input,
|
|
1969
|
+
valid: input,
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
module Top (
|
|
1974
|
+
bus: modport Bus::consumer [2],
|
|
1975
|
+
out: output logic<8>,
|
|
1976
|
+
) {
|
|
1977
|
+
assign out = bus.data[0] + bus.data[1];
|
|
1978
|
+
}
|
|
1979
|
+
`;
|
|
1980
|
+
|
|
1981
|
+
describe("E2E: interface array port (modport [N])", () => {
|
|
1982
|
+
test("interface array members accessible via .at()/.set()", () => {
|
|
1983
|
+
interface TopPorts {
|
|
1984
|
+
bus: {
|
|
1985
|
+
data: {
|
|
1986
|
+
at(i: number): bigint;
|
|
1987
|
+
set(i: number, v: bigint): void;
|
|
1988
|
+
length: number;
|
|
1989
|
+
};
|
|
1990
|
+
valid: {
|
|
1991
|
+
at(i: number): bigint;
|
|
1992
|
+
set(i: number, v: bigint): void;
|
|
1993
|
+
length: number;
|
|
1994
|
+
};
|
|
1995
|
+
};
|
|
1996
|
+
readonly out: bigint;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
const sim = Simulator.fromSource<TopPorts>(INTERFACE_ARRAY_SOURCE, "Top");
|
|
2000
|
+
|
|
2001
|
+
sim.dut.bus.data.set(0, 0x10n);
|
|
2002
|
+
sim.dut.bus.data.set(1, 0x20n);
|
|
2003
|
+
expect(sim.dut.out).toBe(0x30n);
|
|
2004
|
+
|
|
2005
|
+
sim.dut.bus.data.set(0, 0xaan);
|
|
2006
|
+
sim.dut.bus.data.set(1, 0x11n);
|
|
2007
|
+
expect(sim.dut.out).toBe(0xbbn);
|
|
2008
|
+
|
|
2009
|
+
expect(sim.dut.bus.data.length).toBe(2);
|
|
2010
|
+
expect(sim.dut.bus.valid.length).toBe(2);
|
|
2011
|
+
|
|
2012
|
+
sim.dispose();
|
|
2013
|
+
});
|
|
1890
2014
|
});
|
|
1891
2015
|
|
|
1892
2016
|
// ---------------------------------------------------------------------------
|
|
@@ -1912,114 +2036,312 @@ module ArrayPassThrough (
|
|
|
1912
2036
|
}
|
|
1913
2037
|
`;
|
|
1914
2038
|
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
rst: bigint;
|
|
1919
|
-
en: { at(i: number): bigint; set(i: number, v: bigint): void; length: number };
|
|
1920
|
-
readonly y0: bigint;
|
|
1921
|
-
readonly y1: bigint;
|
|
1922
|
-
readonly y2: bigint;
|
|
1923
|
-
readonly y3: bigint;
|
|
1924
|
-
}
|
|
2039
|
+
// ---------------------------------------------------------------------------
|
|
2040
|
+
// Internal var access tests
|
|
2041
|
+
// ---------------------------------------------------------------------------
|
|
1925
2042
|
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
expect(sim.dut.y2).toBe(0n);
|
|
1941
|
-
expect(sim.dut.y3).toBe(0n);
|
|
1942
|
-
|
|
1943
|
-
// Set element 1 (was silently ignored before the fix)
|
|
1944
|
-
sim.dut.en.set(0, 0n);
|
|
1945
|
-
sim.dut.en.set(1, 1n);
|
|
1946
|
-
sim.tick();
|
|
1947
|
-
expect(sim.dut.y0).toBe(0n);
|
|
1948
|
-
expect(sim.dut.y1).toBe(1n);
|
|
1949
|
-
expect(sim.dut.y2).toBe(0n);
|
|
1950
|
-
expect(sim.dut.y3).toBe(0n);
|
|
1951
|
-
|
|
1952
|
-
// Set elements 2 and 3
|
|
1953
|
-
sim.dut.en.set(1, 0n);
|
|
1954
|
-
sim.dut.en.set(2, 1n);
|
|
1955
|
-
sim.dut.en.set(3, 1n);
|
|
1956
|
-
sim.tick();
|
|
1957
|
-
expect(sim.dut.y0).toBe(0n);
|
|
1958
|
-
expect(sim.dut.y1).toBe(0n);
|
|
1959
|
-
expect(sim.dut.y2).toBe(1n);
|
|
1960
|
-
expect(sim.dut.y3).toBe(1n);
|
|
1961
|
-
|
|
1962
|
-
sim.dispose();
|
|
1963
|
-
});
|
|
1964
|
-
|
|
1965
|
-
test("length property is correct", () => {
|
|
1966
|
-
interface Ports {
|
|
1967
|
-
rst: bigint;
|
|
1968
|
-
en: { at(i: number): bigint; set(i: number, v: bigint): void; length: number };
|
|
1969
|
-
readonly y0: bigint;
|
|
1970
|
-
readonly y1: bigint;
|
|
1971
|
-
readonly y2: bigint;
|
|
1972
|
-
readonly y3: bigint;
|
|
2043
|
+
const INTERNAL_VAR_HIERARCHY_SOURCE = `
|
|
2044
|
+
module SubCounter (
|
|
2045
|
+
clk: input clock,
|
|
2046
|
+
rst: input reset,
|
|
2047
|
+
en: input logic,
|
|
2048
|
+
count: output logic<8>,
|
|
2049
|
+
) {
|
|
2050
|
+
var count_r: logic<8>;
|
|
2051
|
+
always_ff (clk, rst) {
|
|
2052
|
+
if_reset {
|
|
2053
|
+
count_r = 0;
|
|
2054
|
+
} else if en {
|
|
2055
|
+
count_r = count_r + 1;
|
|
2056
|
+
}
|
|
1973
2057
|
}
|
|
2058
|
+
assign count = count_r;
|
|
2059
|
+
}
|
|
1974
2060
|
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
2061
|
+
module TopCounter (
|
|
2062
|
+
clk: input clock,
|
|
2063
|
+
rst: input reset,
|
|
2064
|
+
en: input logic,
|
|
2065
|
+
top_count: output logic<8>,
|
|
2066
|
+
) {
|
|
2067
|
+
var top_reg: logic<8>;
|
|
2068
|
+
inst u_sub: SubCounter (
|
|
2069
|
+
clk,
|
|
2070
|
+
rst,
|
|
2071
|
+
en,
|
|
2072
|
+
count: top_count,
|
|
2073
|
+
);
|
|
2074
|
+
always_ff (clk, rst) {
|
|
2075
|
+
if_reset {
|
|
2076
|
+
top_reg = 0;
|
|
2077
|
+
} else {
|
|
2078
|
+
top_reg = top_count;
|
|
2079
|
+
}
|
|
1990
2080
|
}
|
|
2081
|
+
}
|
|
2082
|
+
`;
|
|
2083
|
+
|
|
2084
|
+
describe("E2E: internal var access", () => {
|
|
2085
|
+
test("top-level internal var is accessible and tracks values", () => {
|
|
2086
|
+
const sim = Simulator.fromSource(COUNTER_SOURCE, "Counter");
|
|
2087
|
+
|
|
2088
|
+
// Reset
|
|
2089
|
+
sim.dut.rst = 0n;
|
|
2090
|
+
sim.tick();
|
|
2091
|
+
sim.dut.rst = 1n;
|
|
2092
|
+
sim.tick();
|
|
2093
|
+
|
|
2094
|
+
// Internal var count_r should be accessible and start at 0
|
|
2095
|
+
expect((sim.dut as any).count_r).toBe(0n);
|
|
2096
|
+
|
|
2097
|
+
// Enable counting and tick
|
|
2098
|
+
sim.dut.en = 1n;
|
|
2099
|
+
sim.tick();
|
|
2100
|
+
expect((sim.dut as any).count_r).toBe(1n);
|
|
1991
2101
|
|
|
1992
|
-
|
|
2102
|
+
sim.tick();
|
|
2103
|
+
expect((sim.dut as any).count_r).toBe(2n);
|
|
1993
2104
|
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
sim.tick();
|
|
2105
|
+
// count_r should equal count (output)
|
|
2106
|
+
expect((sim.dut as any).count_r).toBe(sim.dut.count);
|
|
1997
2107
|
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
sim.tick();
|
|
2108
|
+
sim.dispose();
|
|
2109
|
+
});
|
|
2001
2110
|
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2111
|
+
test("child instance internal var is accessible", () => {
|
|
2112
|
+
const sim = Simulator.fromSource(
|
|
2113
|
+
INTERNAL_VAR_HIERARCHY_SOURCE,
|
|
2114
|
+
"TopCounter",
|
|
2115
|
+
);
|
|
2006
2116
|
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2117
|
+
// Reset
|
|
2118
|
+
sim.dut.rst = 0n;
|
|
2119
|
+
sim.tick();
|
|
2120
|
+
sim.dut.rst = 1n;
|
|
2121
|
+
sim.tick();
|
|
2010
2122
|
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
expect(sim.fourState("y0").mask).toBe(0n);
|
|
2014
|
-
expect(sim.fourState("y3").mask).toBe(0n);
|
|
2123
|
+
// Top-level internal var
|
|
2124
|
+
expect((sim.dut as any).top_reg).toBe(0n);
|
|
2015
2125
|
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
sim.tick();
|
|
2126
|
+
// Child instance internal var
|
|
2127
|
+
expect((sim.dut as any).u_sub.count_r).toBe(0n);
|
|
2019
2128
|
|
|
2020
|
-
|
|
2021
|
-
|
|
2129
|
+
// Enable counting
|
|
2130
|
+
sim.dut.en = 1n;
|
|
2131
|
+
sim.tick();
|
|
2132
|
+
expect((sim.dut as any).u_sub.count_r).toBe(1n);
|
|
2133
|
+
|
|
2134
|
+
sim.tick();
|
|
2135
|
+
expect((sim.dut as any).u_sub.count_r).toBe(2n);
|
|
2136
|
+
|
|
2137
|
+
sim.dispose();
|
|
2138
|
+
});
|
|
2139
|
+
});
|
|
2140
|
+
|
|
2141
|
+
describe("E2E: 1-bit unpacked array port (logic [N])", () => {
|
|
2142
|
+
test("each element is independently visible in hardware (issue #6)", () => {
|
|
2143
|
+
interface Ports {
|
|
2144
|
+
rst: bigint;
|
|
2145
|
+
en: {
|
|
2146
|
+
at(i: number): bigint;
|
|
2147
|
+
set(i: number, v: bigint): void;
|
|
2148
|
+
length: number;
|
|
2149
|
+
};
|
|
2150
|
+
readonly y0: bigint;
|
|
2151
|
+
readonly y1: bigint;
|
|
2152
|
+
readonly y2: bigint;
|
|
2153
|
+
readonly y3: bigint;
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
const sim = Simulator.fromSource<Ports>(
|
|
2157
|
+
ARRAY_1BIT_SOURCE,
|
|
2158
|
+
"ArrayPassThrough",
|
|
2159
|
+
);
|
|
2160
|
+
|
|
2161
|
+
// Default: all inputs 0
|
|
2162
|
+
sim.tick();
|
|
2163
|
+
expect(sim.dut.y0).toBe(0n);
|
|
2164
|
+
expect(sim.dut.y1).toBe(0n);
|
|
2165
|
+
expect(sim.dut.y2).toBe(0n);
|
|
2166
|
+
expect(sim.dut.y3).toBe(0n);
|
|
2167
|
+
|
|
2168
|
+
// Set element 0 only
|
|
2169
|
+
sim.dut.en.set(0, 1n);
|
|
2170
|
+
sim.tick();
|
|
2171
|
+
expect(sim.dut.y0).toBe(1n);
|
|
2172
|
+
expect(sim.dut.y1).toBe(0n);
|
|
2173
|
+
expect(sim.dut.y2).toBe(0n);
|
|
2174
|
+
expect(sim.dut.y3).toBe(0n);
|
|
2175
|
+
|
|
2176
|
+
// Set element 1 (was silently ignored before the fix)
|
|
2177
|
+
sim.dut.en.set(0, 0n);
|
|
2178
|
+
sim.dut.en.set(1, 1n);
|
|
2179
|
+
sim.tick();
|
|
2180
|
+
expect(sim.dut.y0).toBe(0n);
|
|
2181
|
+
expect(sim.dut.y1).toBe(1n);
|
|
2182
|
+
expect(sim.dut.y2).toBe(0n);
|
|
2183
|
+
expect(sim.dut.y3).toBe(0n);
|
|
2184
|
+
|
|
2185
|
+
// Set elements 2 and 3
|
|
2186
|
+
sim.dut.en.set(1, 0n);
|
|
2187
|
+
sim.dut.en.set(2, 1n);
|
|
2188
|
+
sim.dut.en.set(3, 1n);
|
|
2189
|
+
sim.tick();
|
|
2190
|
+
expect(sim.dut.y0).toBe(0n);
|
|
2191
|
+
expect(sim.dut.y1).toBe(0n);
|
|
2192
|
+
expect(sim.dut.y2).toBe(1n);
|
|
2193
|
+
expect(sim.dut.y3).toBe(1n);
|
|
2194
|
+
|
|
2195
|
+
sim.dispose();
|
|
2196
|
+
});
|
|
2197
|
+
|
|
2198
|
+
test("length property is correct", () => {
|
|
2199
|
+
interface Ports {
|
|
2200
|
+
rst: bigint;
|
|
2201
|
+
en: {
|
|
2202
|
+
at(i: number): bigint;
|
|
2203
|
+
set(i: number, v: bigint): void;
|
|
2204
|
+
length: number;
|
|
2205
|
+
};
|
|
2206
|
+
readonly y0: bigint;
|
|
2207
|
+
readonly y1: bigint;
|
|
2208
|
+
readonly y2: bigint;
|
|
2209
|
+
readonly y3: bigint;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
const sim = Simulator.fromSource<Ports>(
|
|
2213
|
+
ARRAY_1BIT_SOURCE,
|
|
2214
|
+
"ArrayPassThrough",
|
|
2215
|
+
);
|
|
2216
|
+
expect(sim.dut.en.length).toBe(4);
|
|
2217
|
+
sim.dispose();
|
|
2218
|
+
});
|
|
2219
|
+
|
|
2220
|
+
test("4-state mode: X written to element i propagates only to output yi (verifies maskBase layout)", () => {
|
|
2221
|
+
// This test validates that the maskBase = baseOffset + totalValueBytes assumption
|
|
2222
|
+
// matches the actual JIT memory layout when fourState: true is used.
|
|
2223
|
+
interface Ports {
|
|
2224
|
+
rst: bigint;
|
|
2225
|
+
en: {
|
|
2226
|
+
at(i: number): bigint;
|
|
2227
|
+
set(i: number, v: unknown): void;
|
|
2228
|
+
length: number;
|
|
2229
|
+
};
|
|
2230
|
+
readonly y0: bigint;
|
|
2231
|
+
readonly y1: bigint;
|
|
2232
|
+
readonly y2: bigint;
|
|
2233
|
+
readonly y3: bigint;
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
const sim = Simulator.fromSource<Ports>(
|
|
2237
|
+
ARRAY_1BIT_SOURCE,
|
|
2238
|
+
"ArrayPassThrough",
|
|
2239
|
+
{ fourState: true },
|
|
2240
|
+
);
|
|
2241
|
+
|
|
2242
|
+
// Start with all defined zeros
|
|
2243
|
+
for (let i = 0; i < 4; i++) sim.dut.en.set(i, 0n);
|
|
2244
|
+
sim.tick();
|
|
2245
|
+
|
|
2246
|
+
// Set element 1 to X; only y1 should become X
|
|
2247
|
+
sim.dut.en.set(1, X);
|
|
2248
|
+
sim.tick();
|
|
2249
|
+
|
|
2250
|
+
expect(sim.fourState("y1").mask).not.toBe(0n); // y1 = X
|
|
2251
|
+
expect(sim.fourState("y0").mask).toBe(0n); // y0 defined
|
|
2252
|
+
expect(sim.fourState("y2").mask).toBe(0n); // y2 defined
|
|
2253
|
+
expect(sim.fourState("y3").mask).toBe(0n); // y3 defined
|
|
2254
|
+
|
|
2255
|
+
// Set element 2 to X; y2 becomes X, y1 remains X
|
|
2256
|
+
sim.dut.en.set(2, X);
|
|
2257
|
+
sim.tick();
|
|
2258
|
+
|
|
2259
|
+
expect(sim.fourState("y1").mask).not.toBe(0n);
|
|
2260
|
+
expect(sim.fourState("y2").mask).not.toBe(0n);
|
|
2261
|
+
expect(sim.fourState("y0").mask).toBe(0n);
|
|
2262
|
+
expect(sim.fourState("y3").mask).toBe(0n);
|
|
2263
|
+
|
|
2264
|
+
// Clear element 1 to defined 0; y1 becomes defined again
|
|
2265
|
+
sim.dut.en.set(1, 0n);
|
|
2266
|
+
sim.tick();
|
|
2267
|
+
|
|
2268
|
+
expect(sim.fourState("y1").mask).toBe(0n);
|
|
2269
|
+
expect(sim.fourState("y2").mask).not.toBe(0n);
|
|
2270
|
+
|
|
2271
|
+
sim.dispose();
|
|
2272
|
+
});
|
|
2273
|
+
});
|
|
2274
|
+
|
|
2275
|
+
// ---------------------------------------------------------------------------
|
|
2276
|
+
// Dead Store Elimination (DSE) tests
|
|
2277
|
+
// ---------------------------------------------------------------------------
|
|
2022
2278
|
|
|
2023
|
-
|
|
2024
|
-
|
|
2279
|
+
describe("E2E: deadStorePolicy option", () => {
|
|
2280
|
+
test("preserveTopPorts: top ports work, child instances stripped from DUT", () => {
|
|
2281
|
+
const sim = Simulator.fromSource(HIERARCHY_SOURCE, "Top", {
|
|
2282
|
+
deadStorePolicy: "preserveTopPorts",
|
|
2283
|
+
});
|
|
2284
|
+
|
|
2285
|
+
sim.dut.top_in = 0xabn;
|
|
2286
|
+
sim.tick();
|
|
2287
|
+
expect(sim.dut.top_out).toBe(0xabn);
|
|
2288
|
+
|
|
2289
|
+
// With preserveTopPorts, children should be stripped from hierarchy
|
|
2290
|
+
expect((sim.dut as any).u_sub).toBeUndefined();
|
|
2291
|
+
|
|
2292
|
+
sim.dispose();
|
|
2293
|
+
});
|
|
2294
|
+
|
|
2295
|
+
test("preserveAllPorts: top ports AND child instance ports accessible", () => {
|
|
2296
|
+
const sim = Simulator.fromSource(HIERARCHY_SOURCE, "Top", {
|
|
2297
|
+
deadStorePolicy: "preserveAllPorts",
|
|
2298
|
+
});
|
|
2299
|
+
|
|
2300
|
+
sim.dut.top_in = 0x42n;
|
|
2301
|
+
sim.tick();
|
|
2302
|
+
expect(sim.dut.top_out).toBe(0x42n);
|
|
2303
|
+
|
|
2304
|
+
// With preserveAllPorts, child instance ports should remain accessible
|
|
2305
|
+
expect((sim.dut as any).u_sub).toBeDefined();
|
|
2306
|
+
expect((sim.dut as any).u_sub.o_data).toBe(0x42n);
|
|
2307
|
+
|
|
2308
|
+
sim.dispose();
|
|
2309
|
+
});
|
|
2310
|
+
|
|
2311
|
+
test("Simulation: preserveTopPorts strips children", () => {
|
|
2312
|
+
const sim = Simulation.fromSource(HIERARCHY_SOURCE, "Top", {
|
|
2313
|
+
deadStorePolicy: "preserveTopPorts",
|
|
2314
|
+
});
|
|
2315
|
+
sim.addClock("clk", { period: 10 });
|
|
2316
|
+
|
|
2317
|
+
sim.dut.rst = 0n;
|
|
2318
|
+
sim.runUntil(20);
|
|
2319
|
+
sim.dut.rst = 1n;
|
|
2320
|
+
|
|
2321
|
+
sim.dut.top_in = 0x55n;
|
|
2322
|
+
sim.runUntil(40);
|
|
2323
|
+
expect(sim.dut.top_out).toBe(0x55n);
|
|
2324
|
+
expect((sim.dut as any).u_sub).toBeUndefined();
|
|
2325
|
+
|
|
2326
|
+
sim.dispose();
|
|
2327
|
+
});
|
|
2328
|
+
|
|
2329
|
+
test("Simulation: preserveAllPorts keeps children", () => {
|
|
2330
|
+
const sim = Simulation.fromSource(HIERARCHY_SOURCE, "Top", {
|
|
2331
|
+
deadStorePolicy: "preserveAllPorts",
|
|
2332
|
+
});
|
|
2333
|
+
sim.addClock("clk", { period: 10 });
|
|
2334
|
+
|
|
2335
|
+
sim.dut.rst = 0n;
|
|
2336
|
+
sim.runUntil(20);
|
|
2337
|
+
sim.dut.rst = 1n;
|
|
2338
|
+
|
|
2339
|
+
sim.dut.top_in = 0xffn;
|
|
2340
|
+
sim.runUntil(40);
|
|
2341
|
+
expect(sim.dut.top_out).toBe(0xffn);
|
|
2342
|
+
expect((sim.dut as any).u_sub).toBeDefined();
|
|
2343
|
+
expect((sim.dut as any).u_sub.o_data).toBe(0xffn);
|
|
2344
|
+
|
|
2345
|
+
sim.dispose();
|
|
2346
|
+
});
|
|
2025
2347
|
});
|