@celox-sim/celox 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dut.d.ts +13 -6
- package/dist/dut.d.ts.map +1 -1
- package/dist/dut.js +79 -6
- package/dist/dut.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/napi-helpers.d.ts +62 -1
- package/dist/napi-helpers.d.ts.map +1 -1
- package/dist/napi-helpers.js +154 -19
- package/dist/napi-helpers.js.map +1 -1
- package/dist/simulation.d.ts +59 -3
- package/dist/simulation.d.ts.map +1 -1
- package/dist/simulation.js +162 -16
- package/dist/simulation.js.map +1 -1
- package/dist/simulator.d.ts +7 -1
- package/dist/simulator.d.ts.map +1 -1
- package/dist/simulator.js +31 -13
- package/dist/simulator.js.map +1 -1
- package/dist/types.d.ts +34 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/src/dut.ts +93 -5
- package/src/e2e.bench.ts +164 -1
- package/src/e2e.test.ts +432 -30
- package/src/index.ts +10 -2
- package/src/napi-helpers.ts +224 -22
- package/src/simulation.test.ts +213 -5
- package/src/simulation.ts +198 -13
- package/src/simulator.ts +38 -10
- package/src/types.ts +51 -0
package/src/e2e.test.ts
CHANGED
|
@@ -13,11 +13,12 @@ import { describe, test, expect, afterEach } from "vitest";
|
|
|
13
13
|
import { Simulator } from "./simulator.js";
|
|
14
14
|
import { Simulation } from "./simulation.js";
|
|
15
15
|
import { readFourState } from "./dut.js";
|
|
16
|
-
import { X, FourState } from "./types.js";
|
|
16
|
+
import { X, FourState, SimulationTimeoutError } from "./types.js";
|
|
17
17
|
import {
|
|
18
18
|
createSimulatorBridge,
|
|
19
19
|
loadNativeAddon,
|
|
20
20
|
parseNapiLayout,
|
|
21
|
+
parseSignalPath,
|
|
21
22
|
type RawNapiAddon,
|
|
22
23
|
type RawNapiSimulatorHandle,
|
|
23
24
|
} from "./napi-helpers.js";
|
|
@@ -148,11 +149,11 @@ describe("E2E: Simulator.fromSource (event-based)", () => {
|
|
|
148
149
|
|
|
149
150
|
const sim = Simulator.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
150
151
|
|
|
151
|
-
// Reset the counter
|
|
152
|
-
sim.dut.rst = 1;
|
|
153
|
-
sim.tick();
|
|
152
|
+
// Reset the counter (default async_low: rst=0 is active)
|
|
154
153
|
sim.dut.rst = 0;
|
|
155
154
|
sim.tick();
|
|
155
|
+
sim.dut.rst = 1;
|
|
156
|
+
sim.tick();
|
|
156
157
|
expect(sim.dut.count).toBe(0);
|
|
157
158
|
|
|
158
159
|
// Enable counting
|
|
@@ -229,10 +230,10 @@ describe("E2E: Simulation.fromSource (time-based)", () => {
|
|
|
229
230
|
sim.addClock("clk", { period: 10 });
|
|
230
231
|
expect(sim.time()).toBe(0);
|
|
231
232
|
|
|
232
|
-
// Reset
|
|
233
|
-
sim.dut.rst = 1;
|
|
234
|
-
sim.runUntil(20);
|
|
233
|
+
// Reset (default async_low: rst=0 is active)
|
|
235
234
|
sim.dut.rst = 0;
|
|
235
|
+
sim.runUntil(20);
|
|
236
|
+
sim.dut.rst = 1;
|
|
236
237
|
sim.dut.en = 1;
|
|
237
238
|
|
|
238
239
|
sim.runUntil(100);
|
|
@@ -282,11 +283,11 @@ describe("E2E: Simulator.fromProject (event-based)", () => {
|
|
|
282
283
|
|
|
283
284
|
const sim = Simulator.fromProject<CounterPorts>(COUNTER_PROJECT, "Counter");
|
|
284
285
|
|
|
285
|
-
// Reset the counter
|
|
286
|
-
sim.dut.rst = 1;
|
|
287
|
-
sim.tick();
|
|
286
|
+
// Reset the counter (default async_low: rst=0 is active)
|
|
288
287
|
sim.dut.rst = 0;
|
|
289
288
|
sim.tick();
|
|
289
|
+
sim.dut.rst = 1;
|
|
290
|
+
sim.tick();
|
|
290
291
|
expect(sim.dut.count).toBe(0);
|
|
291
292
|
|
|
292
293
|
// Enable counting
|
|
@@ -321,10 +322,10 @@ describe("E2E: Simulation.fromProject (time-based)", () => {
|
|
|
321
322
|
sim.addClock("clk", { period: 10 });
|
|
322
323
|
expect(sim.time()).toBe(0);
|
|
323
324
|
|
|
324
|
-
// Reset
|
|
325
|
-
sim.dut.rst = 1;
|
|
326
|
-
sim.runUntil(20);
|
|
325
|
+
// Reset (default async_low: rst=0 is active)
|
|
327
326
|
sim.dut.rst = 0;
|
|
327
|
+
sim.runUntil(20);
|
|
328
|
+
sim.dut.rst = 1;
|
|
328
329
|
sim.dut.en = 1;
|
|
329
330
|
|
|
330
331
|
sim.runUntil(100);
|
|
@@ -646,8 +647,8 @@ module InitTest (
|
|
|
646
647
|
const sigQ = layout.forDut.q;
|
|
647
648
|
const clkEventId = events.clk;
|
|
648
649
|
|
|
649
|
-
// 1.
|
|
650
|
-
view.setUint8(sigRst.offset,
|
|
650
|
+
// 1. Assert reset (default async_low: rst=0 is active), d=X
|
|
651
|
+
view.setUint8(sigRst.offset, 0);
|
|
651
652
|
view.setUint8(sigRst.offset + sigRst.byteSize, 0); // rst is defined
|
|
652
653
|
|
|
653
654
|
view.setUint8(sigD.offset, 0);
|
|
@@ -660,8 +661,8 @@ module InitTest (
|
|
|
660
661
|
expect(vQ1).toBe(0);
|
|
661
662
|
expect(mQ1).toBe(0);
|
|
662
663
|
|
|
663
|
-
// 2. Release reset, d = partial X (value=0xA5, mask=0x0F)
|
|
664
|
-
view.setUint8(sigRst.offset,
|
|
664
|
+
// 2. Release reset (rst=1 is inactive), d = partial X (value=0xA5, mask=0x0F)
|
|
665
|
+
view.setUint8(sigRst.offset, 1);
|
|
665
666
|
view.setUint8(sigRst.offset + sigRst.byteSize, 0);
|
|
666
667
|
|
|
667
668
|
view.setUint8(sigD.offset, 0xA5);
|
|
@@ -673,8 +674,8 @@ module InitTest (
|
|
|
673
674
|
const [, mQ2] = readFourState(buf, sigQ);
|
|
674
675
|
expect(mQ2).toBe(0x0F);
|
|
675
676
|
|
|
676
|
-
// 3.
|
|
677
|
-
view.setUint8(sigRst.offset,
|
|
677
|
+
// 3. Assert reset again (rst=0): should clear X
|
|
678
|
+
view.setUint8(sigRst.offset, 0);
|
|
678
679
|
view.setUint8(sigRst.offset + sigRst.byteSize, 0);
|
|
679
680
|
|
|
680
681
|
raw.tick(clkEventId);
|
|
@@ -773,10 +774,11 @@ describe("E2E: 4-state high-level DUT API", () => {
|
|
|
773
774
|
);
|
|
774
775
|
|
|
775
776
|
// In 4-state mode, count starts as X. Reset should clear it.
|
|
776
|
-
|
|
777
|
-
sim.tick();
|
|
777
|
+
// (default async_low: rst=0 is active)
|
|
778
778
|
sim.dut.rst = 0;
|
|
779
779
|
sim.tick();
|
|
780
|
+
sim.dut.rst = 1;
|
|
781
|
+
sim.tick();
|
|
780
782
|
expect(sim.dut.count).toBe(0);
|
|
781
783
|
|
|
782
784
|
// Enable counting — should work exactly like 2-state
|
|
@@ -830,10 +832,10 @@ describe("E2E: 4-state high-level DUT API", () => {
|
|
|
830
832
|
|
|
831
833
|
const sim = Simulator.fromSource<FFPorts>(FF_SOURCE, "FF", { fourState: true });
|
|
832
834
|
|
|
833
|
-
// Reset to clear initial X
|
|
834
|
-
sim.dut.rst = 1;
|
|
835
|
-
sim.tick();
|
|
835
|
+
// Reset to clear initial X (default async_low: rst=0 is active)
|
|
836
836
|
sim.dut.rst = 0;
|
|
837
|
+
sim.tick();
|
|
838
|
+
sim.dut.rst = 1;
|
|
837
839
|
expect(sim.dut.q).toBe(0);
|
|
838
840
|
|
|
839
841
|
// Write a defined value
|
|
@@ -895,10 +897,10 @@ describe("E2E: 4-state Simulation (time-based)", () => {
|
|
|
895
897
|
sim.addClock("clk", { period: 10 });
|
|
896
898
|
expect(sim.time()).toBe(0);
|
|
897
899
|
|
|
898
|
-
// Reset to clear initial X on q
|
|
899
|
-
sim.dut.rst = 1;
|
|
900
|
-
sim.runUntil(20);
|
|
900
|
+
// Reset to clear initial X on q (default async_low: rst=0 is active)
|
|
901
901
|
sim.dut.rst = 0;
|
|
902
|
+
sim.runUntil(20);
|
|
903
|
+
sim.dut.rst = 1;
|
|
902
904
|
expect(sim.dut.q).toBe(0);
|
|
903
905
|
|
|
904
906
|
// Drive d with defined value
|
|
@@ -927,10 +929,10 @@ describe("E2E: 4-state Simulation (time-based)", () => {
|
|
|
927
929
|
|
|
928
930
|
sim.addClock("clk", { period: 10 });
|
|
929
931
|
|
|
930
|
-
// Reset
|
|
931
|
-
sim.dut.rst = 1;
|
|
932
|
-
sim.runUntil(20);
|
|
932
|
+
// Reset (default async_low: rst=0 is active)
|
|
933
933
|
sim.dut.rst = 0;
|
|
934
|
+
sim.runUntil(20);
|
|
935
|
+
sim.dut.rst = 1;
|
|
934
936
|
sim.dut.en = 1;
|
|
935
937
|
|
|
936
938
|
sim.runUntil(100);
|
|
@@ -963,3 +965,403 @@ describe("E2E: 4-state Simulation (time-based)", () => {
|
|
|
963
965
|
sim.dispose();
|
|
964
966
|
});
|
|
965
967
|
});
|
|
968
|
+
|
|
969
|
+
// ---------------------------------------------------------------------------
|
|
970
|
+
// Phase 3b: testbench helpers — Simulation API
|
|
971
|
+
// ---------------------------------------------------------------------------
|
|
972
|
+
|
|
973
|
+
describe("E2E: Simulation testbench helpers", () => {
|
|
974
|
+
test("waitForCycles: advances correct number of clock cycles", () => {
|
|
975
|
+
interface CounterPorts {
|
|
976
|
+
rst: number;
|
|
977
|
+
en: number;
|
|
978
|
+
readonly count: number;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
982
|
+
sim.addClock("clk", { period: 10 });
|
|
983
|
+
|
|
984
|
+
// Reset (default async_low: rst=0 is active)
|
|
985
|
+
sim.dut.rst = 0;
|
|
986
|
+
sim.runUntil(20);
|
|
987
|
+
sim.dut.rst = 1;
|
|
988
|
+
sim.dut.en = 1;
|
|
989
|
+
|
|
990
|
+
const beforeTime = sim.time();
|
|
991
|
+
const afterTime = sim.waitForCycles("clk", 5);
|
|
992
|
+
|
|
993
|
+
expect(afterTime).toBeGreaterThan(beforeTime);
|
|
994
|
+
// Each cycle = 2 steps with period 10, so 5 cycles ≈ 50 time units
|
|
995
|
+
expect(afterTime - beforeTime).toBe(50);
|
|
996
|
+
|
|
997
|
+
sim.dispose();
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
test("waitUntil: waits for condition to be met", () => {
|
|
1001
|
+
interface CounterPorts {
|
|
1002
|
+
rst: number;
|
|
1003
|
+
en: number;
|
|
1004
|
+
readonly count: number;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
1008
|
+
sim.addClock("clk", { period: 10 });
|
|
1009
|
+
|
|
1010
|
+
// Reset (default async_low: rst=0 is active)
|
|
1011
|
+
sim.dut.rst = 0;
|
|
1012
|
+
sim.runUntil(20);
|
|
1013
|
+
sim.dut.rst = 1;
|
|
1014
|
+
sim.dut.en = 1;
|
|
1015
|
+
|
|
1016
|
+
const t = sim.waitUntil(() => sim.dut.count >= 3);
|
|
1017
|
+
expect(sim.dut.count).toBeGreaterThanOrEqual(3);
|
|
1018
|
+
expect(t).toBeGreaterThan(20);
|
|
1019
|
+
|
|
1020
|
+
sim.dispose();
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
test("waitUntil: throws SimulationTimeoutError on timeout", () => {
|
|
1024
|
+
interface CounterPorts {
|
|
1025
|
+
rst: number;
|
|
1026
|
+
en: number;
|
|
1027
|
+
readonly count: number;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
1031
|
+
sim.addClock("clk", { period: 10 });
|
|
1032
|
+
|
|
1033
|
+
// Reset (default async_low: rst=0 is active)
|
|
1034
|
+
sim.dut.rst = 0;
|
|
1035
|
+
sim.runUntil(20);
|
|
1036
|
+
sim.dut.rst = 1;
|
|
1037
|
+
sim.dut.en = 0; // disabled — count won't increase
|
|
1038
|
+
|
|
1039
|
+
expect(() =>
|
|
1040
|
+
sim.waitUntil(() => sim.dut.count >= 100, { maxSteps: 20 }),
|
|
1041
|
+
).toThrow(SimulationTimeoutError);
|
|
1042
|
+
|
|
1043
|
+
sim.dispose();
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
test("reset: asserts and releases reset on counter", () => {
|
|
1047
|
+
interface CounterPorts {
|
|
1048
|
+
rst: number;
|
|
1049
|
+
en: number;
|
|
1050
|
+
readonly count: number;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
1054
|
+
sim.addClock("clk", { period: 10 });
|
|
1055
|
+
|
|
1056
|
+
// Count up a bit (default async_low: rst=0 is active)
|
|
1057
|
+
sim.dut.rst = 0;
|
|
1058
|
+
sim.runUntil(20);
|
|
1059
|
+
sim.dut.rst = 1;
|
|
1060
|
+
sim.dut.en = 1;
|
|
1061
|
+
sim.runUntil(100);
|
|
1062
|
+
expect(sim.dut.count).toBeGreaterThan(0);
|
|
1063
|
+
|
|
1064
|
+
// Reset using the helper
|
|
1065
|
+
sim.reset("rst");
|
|
1066
|
+
expect(sim.dut.count).toBe(0);
|
|
1067
|
+
|
|
1068
|
+
sim.dispose();
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
test("reset: explicit async_low resetType activates with 0", () => {
|
|
1072
|
+
interface CounterPorts {
|
|
1073
|
+
rst: number;
|
|
1074
|
+
en: number;
|
|
1075
|
+
readonly count: number;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter", {
|
|
1079
|
+
resetType: "async_low",
|
|
1080
|
+
});
|
|
1081
|
+
sim.addClock("clk", { period: 10 });
|
|
1082
|
+
|
|
1083
|
+
// With async_low, rst=0 is active, rst=1 is inactive
|
|
1084
|
+
sim.reset("rst");
|
|
1085
|
+
|
|
1086
|
+
sim.dut.en = 1;
|
|
1087
|
+
sim.runUntil(100);
|
|
1088
|
+
expect(sim.dut.count).toBeGreaterThan(0);
|
|
1089
|
+
|
|
1090
|
+
// Reset again using helper, verify it resets the counter
|
|
1091
|
+
sim.reset("rst");
|
|
1092
|
+
expect(sim.dut.count).toBe(0);
|
|
1093
|
+
|
|
1094
|
+
sim.dispose();
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
test("reset: explicit async_high resetType activates with 1", () => {
|
|
1098
|
+
interface CounterPorts {
|
|
1099
|
+
rst: number;
|
|
1100
|
+
en: number;
|
|
1101
|
+
readonly count: number;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter", {
|
|
1105
|
+
resetType: "async_high",
|
|
1106
|
+
});
|
|
1107
|
+
sim.addClock("clk", { period: 10 });
|
|
1108
|
+
|
|
1109
|
+
// With async_high, rst=1 is active, rst=0 is inactive
|
|
1110
|
+
sim.reset("rst");
|
|
1111
|
+
|
|
1112
|
+
sim.dut.en = 1;
|
|
1113
|
+
sim.runUntil(100);
|
|
1114
|
+
expect(sim.dut.count).toBeGreaterThan(0);
|
|
1115
|
+
|
|
1116
|
+
// Reset again
|
|
1117
|
+
sim.reset("rst");
|
|
1118
|
+
expect(sim.dut.count).toBe(0);
|
|
1119
|
+
|
|
1120
|
+
sim.dispose();
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
test("Simulator.fromSource: resetType option works", () => {
|
|
1124
|
+
interface CounterPorts {
|
|
1125
|
+
rst: number;
|
|
1126
|
+
en: number;
|
|
1127
|
+
readonly count: number;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
const sim = Simulator.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter", {
|
|
1131
|
+
resetType: "async_high",
|
|
1132
|
+
});
|
|
1133
|
+
|
|
1134
|
+
// With async_high, assert reset with 1
|
|
1135
|
+
sim.dut.rst = 1;
|
|
1136
|
+
sim.tick();
|
|
1137
|
+
sim.dut.rst = 0;
|
|
1138
|
+
sim.tick();
|
|
1139
|
+
expect(sim.dut.count).toBe(0);
|
|
1140
|
+
|
|
1141
|
+
// Count up
|
|
1142
|
+
sim.dut.en = 1;
|
|
1143
|
+
sim.tick();
|
|
1144
|
+
expect(sim.dut.count).toBe(1);
|
|
1145
|
+
|
|
1146
|
+
sim.dispose();
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
test("runUntil with maxSteps: succeeds within budget", () => {
|
|
1150
|
+
interface CounterPorts {
|
|
1151
|
+
rst: number;
|
|
1152
|
+
en: number;
|
|
1153
|
+
readonly count: number;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
1157
|
+
sim.addClock("clk", { period: 10 });
|
|
1158
|
+
|
|
1159
|
+
// Reset (default async_low: rst=0 is active)
|
|
1160
|
+
sim.dut.rst = 0;
|
|
1161
|
+
sim.runUntil(20);
|
|
1162
|
+
sim.dut.rst = 1;
|
|
1163
|
+
sim.dut.en = 1;
|
|
1164
|
+
|
|
1165
|
+
// 100 time units with period 10 = 10 events, should fit in 100 steps
|
|
1166
|
+
sim.runUntil(120, { maxSteps: 100 });
|
|
1167
|
+
expect(sim.time()).toBe(120);
|
|
1168
|
+
|
|
1169
|
+
sim.dispose();
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
test("runUntil with maxSteps: throws on exceeded budget", () => {
|
|
1173
|
+
interface CounterPorts {
|
|
1174
|
+
rst: number;
|
|
1175
|
+
en: number;
|
|
1176
|
+
readonly count: number;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter");
|
|
1180
|
+
sim.addClock("clk", { period: 10 });
|
|
1181
|
+
|
|
1182
|
+
// Release reset so counter can count (default async_low: rst=1 is inactive)
|
|
1183
|
+
sim.dut.rst = 1;
|
|
1184
|
+
sim.dut.en = 1;
|
|
1185
|
+
|
|
1186
|
+
// Very small budget for a long run
|
|
1187
|
+
expect(() => sim.runUntil(100000, { maxSteps: 5 })).toThrow(
|
|
1188
|
+
SimulationTimeoutError,
|
|
1189
|
+
);
|
|
1190
|
+
|
|
1191
|
+
sim.dispose();
|
|
1192
|
+
});
|
|
1193
|
+
});
|
|
1194
|
+
|
|
1195
|
+
// ---------------------------------------------------------------------------
|
|
1196
|
+
// Phase 3b: fourState() method
|
|
1197
|
+
// ---------------------------------------------------------------------------
|
|
1198
|
+
|
|
1199
|
+
describe("E2E: fourState() method", () => {
|
|
1200
|
+
test("Simulator.fourState: reads 4-state value and mask", () => {
|
|
1201
|
+
interface Ports {
|
|
1202
|
+
a: number;
|
|
1203
|
+
b: number;
|
|
1204
|
+
readonly y: number;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
const sim = Simulator.fromSource<Ports>(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
1208
|
+
fourState: true,
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
sim.dut.a = 100;
|
|
1212
|
+
sim.dut.b = 55;
|
|
1213
|
+
// Trigger evalComb via output read (Adder4S is purely combinational)
|
|
1214
|
+
expect(sim.dut.y).toBe(155);
|
|
1215
|
+
|
|
1216
|
+
const fs = sim.fourState("y");
|
|
1217
|
+
expect(fs.__fourState).toBe(true);
|
|
1218
|
+
expect(fs.value).toBe(155);
|
|
1219
|
+
expect(fs.mask).toBe(0);
|
|
1220
|
+
|
|
1221
|
+
sim.dispose();
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
test("Simulator.fourState: reads X mask when input is X", () => {
|
|
1225
|
+
interface Ports {
|
|
1226
|
+
a: number;
|
|
1227
|
+
b: number;
|
|
1228
|
+
readonly y: number;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
const sim = Simulator.fromSource<Ports>(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
1232
|
+
fourState: true,
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
(sim.dut as any).a = X;
|
|
1236
|
+
sim.dut.b = 10;
|
|
1237
|
+
// Trigger evalComb via output read
|
|
1238
|
+
sim.dut.y;
|
|
1239
|
+
|
|
1240
|
+
const fs = sim.fourState("y");
|
|
1241
|
+
expect(fs.mask).toBe(0xFF); // all X from arithmetic propagation
|
|
1242
|
+
|
|
1243
|
+
sim.dispose();
|
|
1244
|
+
});
|
|
1245
|
+
|
|
1246
|
+
test("Simulation.fourState: reads 4-state value", () => {
|
|
1247
|
+
interface Ports {
|
|
1248
|
+
a: number;
|
|
1249
|
+
b: number;
|
|
1250
|
+
readonly y: number;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
const sim = Simulation.fromSource<Ports>(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
1254
|
+
fourState: true,
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1257
|
+
sim.dut.a = 50;
|
|
1258
|
+
sim.dut.b = 25;
|
|
1259
|
+
sim.runUntil(0);
|
|
1260
|
+
|
|
1261
|
+
const fs = sim.fourState("y");
|
|
1262
|
+
expect(fs.value).toBe(75);
|
|
1263
|
+
expect(fs.mask).toBe(0);
|
|
1264
|
+
|
|
1265
|
+
sim.dispose();
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
test("fourState: throws for unknown port", () => {
|
|
1269
|
+
interface Ports {
|
|
1270
|
+
a: number;
|
|
1271
|
+
b: number;
|
|
1272
|
+
readonly y: number;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
const sim = Simulator.fromSource<Ports>(ADDER_4STATE_SOURCE, "Adder4S", {
|
|
1276
|
+
fourState: true,
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
expect(() => sim.fourState("nonexistent")).toThrow("Unknown port");
|
|
1280
|
+
|
|
1281
|
+
sim.dispose();
|
|
1282
|
+
});
|
|
1283
|
+
});
|
|
1284
|
+
|
|
1285
|
+
// ---------------------------------------------------------------------------
|
|
1286
|
+
// Phase 3b: optimize flag
|
|
1287
|
+
// ---------------------------------------------------------------------------
|
|
1288
|
+
|
|
1289
|
+
describe("E2E: optimize flag", () => {
|
|
1290
|
+
test("Simulator.fromSource with optimize: true", () => {
|
|
1291
|
+
interface AdderPorts {
|
|
1292
|
+
rst: number;
|
|
1293
|
+
a: number;
|
|
1294
|
+
b: number;
|
|
1295
|
+
readonly sum: number;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
const sim = Simulator.fromSource<AdderPorts>(ADDER_SOURCE, "Adder", {
|
|
1299
|
+
optimize: true,
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
sim.dut.a = 100;
|
|
1303
|
+
sim.dut.b = 200;
|
|
1304
|
+
sim.tick();
|
|
1305
|
+
expect(sim.dut.sum).toBe(300);
|
|
1306
|
+
|
|
1307
|
+
sim.dispose();
|
|
1308
|
+
});
|
|
1309
|
+
|
|
1310
|
+
test("Simulation.fromSource with optimize: true", () => {
|
|
1311
|
+
interface CounterPorts {
|
|
1312
|
+
rst: number;
|
|
1313
|
+
en: number;
|
|
1314
|
+
readonly count: number;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
const sim = Simulation.fromSource<CounterPorts>(COUNTER_SOURCE, "Counter", {
|
|
1318
|
+
optimize: true,
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
sim.addClock("clk", { period: 10 });
|
|
1322
|
+
|
|
1323
|
+
// Reset (default async_low: rst=0 is active)
|
|
1324
|
+
sim.dut.rst = 0;
|
|
1325
|
+
sim.runUntil(20);
|
|
1326
|
+
sim.dut.rst = 1;
|
|
1327
|
+
sim.dut.en = 1;
|
|
1328
|
+
sim.runUntil(100);
|
|
1329
|
+
|
|
1330
|
+
expect(sim.dut.count).toBeGreaterThan(0);
|
|
1331
|
+
|
|
1332
|
+
sim.dispose();
|
|
1333
|
+
});
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
// ---------------------------------------------------------------------------
|
|
1337
|
+
// parseSignalPath unit tests
|
|
1338
|
+
// ---------------------------------------------------------------------------
|
|
1339
|
+
|
|
1340
|
+
describe("parseSignalPath", () => {
|
|
1341
|
+
test("simple variable path", () => {
|
|
1342
|
+
const result = parseSignalPath("v");
|
|
1343
|
+
expect(result.instancePath).toEqual([]);
|
|
1344
|
+
expect(result.varPath).toEqual(["v"]);
|
|
1345
|
+
});
|
|
1346
|
+
|
|
1347
|
+
test("instance:variable split", () => {
|
|
1348
|
+
const result = parseSignalPath("p2:i");
|
|
1349
|
+
expect(result.instancePath).toEqual([{ name: "p2", index: 0 }]);
|
|
1350
|
+
expect(result.varPath).toEqual(["i"]);
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
test("nested instance with array index", () => {
|
|
1354
|
+
const result = parseSignalPath("a.b[3]:x.y");
|
|
1355
|
+
expect(result.instancePath).toEqual([
|
|
1356
|
+
{ name: "a", index: 0 },
|
|
1357
|
+
{ name: "b", index: 3 },
|
|
1358
|
+
]);
|
|
1359
|
+
expect(result.varPath).toEqual(["x", "y"]);
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
test("dotted variable path without instance", () => {
|
|
1363
|
+
const result = parseSignalPath("foo.bar.baz");
|
|
1364
|
+
expect(result.instancePath).toEqual([]);
|
|
1365
|
+
expect(result.varPath).toEqual(["foo", "bar", "baz"]);
|
|
1366
|
+
});
|
|
1367
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,8 @@ export type {
|
|
|
15
15
|
SimulatorOptions,
|
|
16
16
|
EventHandle,
|
|
17
17
|
FourStateValue,
|
|
18
|
+
LoopBreak,
|
|
19
|
+
TrueLoopSpec,
|
|
18
20
|
} from "./types.js";
|
|
19
21
|
|
|
20
22
|
/** @internal */
|
|
@@ -29,6 +31,9 @@ export type {
|
|
|
29
31
|
// 4-state helpers
|
|
30
32
|
export { X, FourState, isFourStateValue } from "./types.js";
|
|
31
33
|
|
|
34
|
+
// Error types
|
|
35
|
+
export { SimulationTimeoutError } from "./types.js";
|
|
36
|
+
|
|
32
37
|
// Simulator (event-based)
|
|
33
38
|
export { Simulator } from "./simulator.js";
|
|
34
39
|
|
|
@@ -36,7 +41,7 @@ export { Simulator } from "./simulator.js";
|
|
|
36
41
|
export { Simulation } from "./simulation.js";
|
|
37
42
|
|
|
38
43
|
/** @internal */
|
|
39
|
-
export { createDut, readFourState } from "./dut.js";
|
|
44
|
+
export { createDut, createChildDut, readFourState } from "./dut.js";
|
|
40
45
|
/** @internal */
|
|
41
46
|
export type { DirtyState } from "./dut.js";
|
|
42
47
|
|
|
@@ -44,14 +49,17 @@ export type { DirtyState } from "./dut.js";
|
|
|
44
49
|
export {
|
|
45
50
|
loadNativeAddon,
|
|
46
51
|
parseNapiLayout,
|
|
52
|
+
parseHierarchyLayout,
|
|
47
53
|
buildPortsFromLayout,
|
|
48
54
|
wrapDirectSimulatorHandle,
|
|
49
55
|
wrapDirectSimulationHandle,
|
|
50
56
|
createSimulatorBridge,
|
|
51
57
|
createSimulationBridge,
|
|
58
|
+
parseSignalPath,
|
|
59
|
+
buildNapiOpts,
|
|
52
60
|
} from "./napi-helpers.js";
|
|
53
61
|
/** @internal */
|
|
54
|
-
export type { RawNapiAddon, RawNapiSimulatorHandle, RawNapiSimulationHandle } from "./napi-helpers.js";
|
|
62
|
+
export type { HierarchyNode, RawNapiAddon, RawNapiSimulatorHandle, RawNapiSimulationHandle } from "./napi-helpers.js";
|
|
55
63
|
|
|
56
64
|
// NAPI bridge (backward compat — re-exports from napi-helpers)
|
|
57
65
|
// Consumers that import from "./napi-bridge.js" still work.
|