@celox-sim/celox 0.1.13 → 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 +23 -10
- package/dist/dut.js.map +1 -1
- package/dist/index.d.ts +8 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -10
- package/dist/index.js.map +1 -1
- package/dist/napi-bridge.d.ts +1 -1
- package/dist/napi-bridge.d.ts.map +1 -1
- package/dist/napi-bridge.js +1 -1
- package/dist/napi-bridge.js.map +1 -1
- package/dist/napi-helpers.d.ts +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 +1037 -742
- package/src/dut.ts +580 -528
- package/src/e2e.bench.ts +512 -509
- package/src/e2e.test.ts +1847 -1574
- 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/dut.test.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import { createDut,
|
|
3
|
-
import type {
|
|
4
|
-
NativeSimulatorHandle,
|
|
5
|
-
PortInfo,
|
|
6
|
-
SignalLayout,
|
|
7
|
-
} from "./types.js";
|
|
1
|
+
import { describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { createDut, type DirtyState, readFourState } from "./dut.js";
|
|
3
|
+
import type { NativeSimulatorHandle, PortInfo, SignalLayout } from "./types.js";
|
|
8
4
|
import { FourState, X } from "./types.js";
|
|
9
5
|
|
|
10
6
|
// ---------------------------------------------------------------------------
|
|
@@ -12,17 +8,17 @@ import { FourState, X } from "./types.js";
|
|
|
12
8
|
// ---------------------------------------------------------------------------
|
|
13
9
|
|
|
14
10
|
function mockHandle(): NativeSimulatorHandle {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
return {
|
|
12
|
+
tick: vi.fn(),
|
|
13
|
+
tickN: vi.fn(),
|
|
14
|
+
evalComb: vi.fn(),
|
|
15
|
+
dump: vi.fn(),
|
|
16
|
+
dispose: vi.fn(),
|
|
17
|
+
};
|
|
22
18
|
}
|
|
23
19
|
|
|
24
20
|
function makeBuffer(size: number): SharedArrayBuffer {
|
|
25
|
-
|
|
21
|
+
return new SharedArrayBuffer(size);
|
|
26
22
|
}
|
|
27
23
|
|
|
28
24
|
// ---------------------------------------------------------------------------
|
|
@@ -30,112 +26,148 @@ function makeBuffer(size: number): SharedArrayBuffer {
|
|
|
30
26
|
// ---------------------------------------------------------------------------
|
|
31
27
|
|
|
32
28
|
describe("createDut — scalar ports", () => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
29
|
+
test("write and read 8-bit input", () => {
|
|
30
|
+
const buffer = makeBuffer(64);
|
|
31
|
+
const layout: Record<string, SignalLayout> = {
|
|
32
|
+
a: {
|
|
33
|
+
offset: 0,
|
|
34
|
+
width: 8,
|
|
35
|
+
byteSize: 1,
|
|
36
|
+
is4state: false,
|
|
37
|
+
direction: "input",
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
const ports: Record<string, PortInfo> = {
|
|
41
|
+
a: { direction: "input", type: "logic", width: 8 },
|
|
42
|
+
};
|
|
43
|
+
const handle = mockHandle();
|
|
44
|
+
const state: DirtyState = { dirty: false };
|
|
45
|
+
|
|
46
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
47
|
+
|
|
48
|
+
dut.a = 42n;
|
|
49
|
+
expect(state.dirty).toBe(true);
|
|
50
|
+
expect(dut.a).toBe(42n);
|
|
51
|
+
// Reading an input doesn't trigger evalComb
|
|
52
|
+
expect(handle.evalComb).not.toHaveBeenCalled();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("write and read 16-bit input", () => {
|
|
56
|
+
const buffer = makeBuffer(64);
|
|
57
|
+
const layout: Record<string, SignalLayout> = {
|
|
58
|
+
a: {
|
|
59
|
+
offset: 0,
|
|
60
|
+
width: 16,
|
|
61
|
+
byteSize: 2,
|
|
62
|
+
is4state: false,
|
|
63
|
+
direction: "input",
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
const ports: Record<string, PortInfo> = {
|
|
67
|
+
a: { direction: "input", type: "logic", width: 16 },
|
|
68
|
+
};
|
|
69
|
+
const handle = mockHandle();
|
|
70
|
+
const state: DirtyState = { dirty: false };
|
|
71
|
+
|
|
72
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
73
|
+
|
|
74
|
+
dut.a = 0xabcdn;
|
|
75
|
+
expect(dut.a).toBe(0xabcdn);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("write and read 32-bit input", () => {
|
|
79
|
+
const buffer = makeBuffer(64);
|
|
80
|
+
const layout: Record<string, SignalLayout> = {
|
|
81
|
+
a: {
|
|
82
|
+
offset: 0,
|
|
83
|
+
width: 32,
|
|
84
|
+
byteSize: 4,
|
|
85
|
+
is4state: false,
|
|
86
|
+
direction: "input",
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
const ports: Record<string, PortInfo> = {
|
|
90
|
+
a: { direction: "input", type: "logic", width: 32 },
|
|
91
|
+
};
|
|
92
|
+
const handle = mockHandle();
|
|
93
|
+
const state: DirtyState = { dirty: false };
|
|
94
|
+
|
|
95
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
96
|
+
|
|
97
|
+
dut.a = 0xdead_beefn;
|
|
98
|
+
expect(dut.a).toBe(0xdead_beefn);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("write and read 48-bit value", () => {
|
|
102
|
+
const buffer = makeBuffer(64);
|
|
103
|
+
const layout: Record<string, SignalLayout> = {
|
|
104
|
+
a: {
|
|
105
|
+
offset: 0,
|
|
106
|
+
width: 48,
|
|
107
|
+
byteSize: 8,
|
|
108
|
+
is4state: false,
|
|
109
|
+
direction: "input",
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
const ports: Record<string, PortInfo> = {
|
|
113
|
+
a: { direction: "input", type: "logic", width: 48 },
|
|
114
|
+
};
|
|
115
|
+
const handle = mockHandle();
|
|
116
|
+
const state: DirtyState = { dirty: false };
|
|
117
|
+
|
|
118
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
119
|
+
|
|
120
|
+
const val = 0x1234_5678_9abcn;
|
|
121
|
+
dut.a = val;
|
|
122
|
+
expect(dut.a).toBe(val);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("write and read 64-bit BigInt value", () => {
|
|
126
|
+
const buffer = makeBuffer(64);
|
|
127
|
+
const layout: Record<string, SignalLayout> = {
|
|
128
|
+
a: {
|
|
129
|
+
offset: 0,
|
|
130
|
+
width: 64,
|
|
131
|
+
byteSize: 8,
|
|
132
|
+
is4state: false,
|
|
133
|
+
direction: "input",
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
const ports: Record<string, PortInfo> = {
|
|
137
|
+
a: { direction: "input", type: "logic", width: 64 },
|
|
138
|
+
};
|
|
139
|
+
const handle = mockHandle();
|
|
140
|
+
const state: DirtyState = { dirty: false };
|
|
141
|
+
|
|
142
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
143
|
+
|
|
144
|
+
const val = 0xdead_beef_cafe_baben;
|
|
145
|
+
(dut as any).a = val;
|
|
146
|
+
expect(dut.a).toBe(val);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("8-bit write masks to width", () => {
|
|
150
|
+
const buffer = makeBuffer(64);
|
|
151
|
+
const layout: Record<string, SignalLayout> = {
|
|
152
|
+
a: {
|
|
153
|
+
offset: 0,
|
|
154
|
+
width: 4,
|
|
155
|
+
byteSize: 1,
|
|
156
|
+
is4state: false,
|
|
157
|
+
direction: "input",
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
const ports: Record<string, PortInfo> = {
|
|
161
|
+
a: { direction: "input", type: "logic", width: 4 },
|
|
162
|
+
};
|
|
163
|
+
const handle = mockHandle();
|
|
164
|
+
const state: DirtyState = { dirty: false };
|
|
165
|
+
|
|
166
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
167
|
+
|
|
168
|
+
dut.a = 0xffn; // Only lower 4 bits should be stored
|
|
169
|
+
expect(dut.a).toBe(0x0fn);
|
|
170
|
+
});
|
|
139
171
|
});
|
|
140
172
|
|
|
141
173
|
// ---------------------------------------------------------------------------
|
|
@@ -143,86 +175,130 @@ describe("createDut — scalar ports", () => {
|
|
|
143
175
|
// ---------------------------------------------------------------------------
|
|
144
176
|
|
|
145
177
|
describe("createDut — dirty tracking", () => {
|
|
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
|
-
|
|
178
|
+
test("reading output when dirty triggers evalComb", () => {
|
|
179
|
+
const buffer = makeBuffer(64);
|
|
180
|
+
const layout: Record<string, SignalLayout> = {
|
|
181
|
+
a: {
|
|
182
|
+
offset: 0,
|
|
183
|
+
width: 16,
|
|
184
|
+
byteSize: 2,
|
|
185
|
+
is4state: false,
|
|
186
|
+
direction: "input",
|
|
187
|
+
},
|
|
188
|
+
sum: {
|
|
189
|
+
offset: 4,
|
|
190
|
+
width: 17,
|
|
191
|
+
byteSize: 4,
|
|
192
|
+
is4state: false,
|
|
193
|
+
direction: "output",
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
const ports: Record<string, PortInfo> = {
|
|
197
|
+
a: { direction: "input", type: "logic", width: 16 },
|
|
198
|
+
sum: { direction: "output", type: "logic", width: 17 },
|
|
199
|
+
};
|
|
200
|
+
const handle = mockHandle();
|
|
201
|
+
const state: DirtyState = { dirty: false };
|
|
202
|
+
|
|
203
|
+
const dut = createDut<{ a: bigint; readonly sum: bigint }>(
|
|
204
|
+
buffer,
|
|
205
|
+
layout,
|
|
206
|
+
ports,
|
|
207
|
+
handle,
|
|
208
|
+
state,
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
// Write input → dirty
|
|
212
|
+
dut.a = 100n;
|
|
213
|
+
expect(state.dirty).toBe(true);
|
|
214
|
+
|
|
215
|
+
// Read output → evalComb should be called
|
|
216
|
+
void dut.sum;
|
|
217
|
+
expect(handle.evalComb).toHaveBeenCalledTimes(1);
|
|
218
|
+
expect(state.dirty).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("reading output when clean does NOT trigger evalComb", () => {
|
|
222
|
+
const buffer = makeBuffer(64);
|
|
223
|
+
const layout: Record<string, SignalLayout> = {
|
|
224
|
+
sum: {
|
|
225
|
+
offset: 0,
|
|
226
|
+
width: 17,
|
|
227
|
+
byteSize: 4,
|
|
228
|
+
is4state: false,
|
|
229
|
+
direction: "output",
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
const ports: Record<string, PortInfo> = {
|
|
233
|
+
sum: { direction: "output", type: "logic", width: 17 },
|
|
234
|
+
};
|
|
235
|
+
const handle = mockHandle();
|
|
236
|
+
const state: DirtyState = { dirty: false };
|
|
237
|
+
|
|
238
|
+
const dut = createDut<{ readonly sum: bigint }>(
|
|
239
|
+
buffer,
|
|
240
|
+
layout,
|
|
241
|
+
ports,
|
|
242
|
+
handle,
|
|
243
|
+
state,
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
void dut.sum;
|
|
247
|
+
expect(handle.evalComb).not.toHaveBeenCalled();
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("reading input does NOT trigger evalComb even when dirty", () => {
|
|
251
|
+
const buffer = makeBuffer(64);
|
|
252
|
+
const layout: Record<string, SignalLayout> = {
|
|
253
|
+
a: {
|
|
254
|
+
offset: 0,
|
|
255
|
+
width: 8,
|
|
256
|
+
byteSize: 1,
|
|
257
|
+
is4state: false,
|
|
258
|
+
direction: "input",
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
const ports: Record<string, PortInfo> = {
|
|
262
|
+
a: { direction: "input", type: "logic", width: 8 },
|
|
263
|
+
};
|
|
264
|
+
const handle = mockHandle();
|
|
265
|
+
const state: DirtyState = { dirty: true };
|
|
266
|
+
|
|
267
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
268
|
+
|
|
269
|
+
void dut.a;
|
|
270
|
+
expect(handle.evalComb).not.toHaveBeenCalled();
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("writing to output throws", () => {
|
|
274
|
+
const buffer = makeBuffer(64);
|
|
275
|
+
const layout: Record<string, SignalLayout> = {
|
|
276
|
+
sum: {
|
|
277
|
+
offset: 0,
|
|
278
|
+
width: 17,
|
|
279
|
+
byteSize: 4,
|
|
280
|
+
is4state: false,
|
|
281
|
+
direction: "output",
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
const ports: Record<string, PortInfo> = {
|
|
285
|
+
sum: { direction: "output", type: "logic", width: 17 },
|
|
286
|
+
};
|
|
287
|
+
const handle = mockHandle();
|
|
288
|
+
const state: DirtyState = { dirty: false };
|
|
289
|
+
|
|
290
|
+
const dut = createDut<{ sum: bigint }>(
|
|
291
|
+
buffer,
|
|
292
|
+
layout,
|
|
293
|
+
ports,
|
|
294
|
+
handle,
|
|
295
|
+
state,
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
expect(() => {
|
|
299
|
+
dut.sum = 42n;
|
|
300
|
+
}).toThrow("Cannot write to output port 'sum'");
|
|
301
|
+
});
|
|
226
302
|
});
|
|
227
303
|
|
|
228
304
|
// ---------------------------------------------------------------------------
|
|
@@ -230,24 +306,36 @@ describe("createDut — dirty tracking", () => {
|
|
|
230
306
|
// ---------------------------------------------------------------------------
|
|
231
307
|
|
|
232
308
|
describe("createDut — clock ports", () => {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
309
|
+
test("clock ports are not exposed on the DUT", () => {
|
|
310
|
+
const buffer = makeBuffer(64);
|
|
311
|
+
const layout: Record<string, SignalLayout> = {
|
|
312
|
+
clk: {
|
|
313
|
+
offset: 0,
|
|
314
|
+
width: 1,
|
|
315
|
+
byteSize: 1,
|
|
316
|
+
is4state: false,
|
|
317
|
+
direction: "input",
|
|
318
|
+
},
|
|
319
|
+
a: {
|
|
320
|
+
offset: 1,
|
|
321
|
+
width: 8,
|
|
322
|
+
byteSize: 1,
|
|
323
|
+
is4state: false,
|
|
324
|
+
direction: "input",
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
const ports: Record<string, PortInfo> = {
|
|
328
|
+
clk: { direction: "input", type: "clock", width: 1 },
|
|
329
|
+
a: { direction: "input", type: "logic", width: 8 },
|
|
330
|
+
};
|
|
331
|
+
const handle = mockHandle();
|
|
332
|
+
const state: DirtyState = { dirty: false };
|
|
333
|
+
|
|
334
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
335
|
+
|
|
336
|
+
expect(Object.keys(dut as object)).toEqual(["a"]);
|
|
337
|
+
expect((dut as any).clk).toBeUndefined();
|
|
338
|
+
});
|
|
251
339
|
});
|
|
252
340
|
|
|
253
341
|
// ---------------------------------------------------------------------------
|
|
@@ -255,48 +343,72 @@ describe("createDut — clock ports", () => {
|
|
|
255
343
|
// ---------------------------------------------------------------------------
|
|
256
344
|
|
|
257
345
|
describe("createDut — multiple signals", () => {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
346
|
+
test("Adder-like module with a, b, sum", () => {
|
|
347
|
+
const buffer = makeBuffer(64);
|
|
348
|
+
const layout: Record<string, SignalLayout> = {
|
|
349
|
+
rst: {
|
|
350
|
+
offset: 0,
|
|
351
|
+
width: 1,
|
|
352
|
+
byteSize: 1,
|
|
353
|
+
is4state: false,
|
|
354
|
+
direction: "input",
|
|
355
|
+
},
|
|
356
|
+
a: {
|
|
357
|
+
offset: 2,
|
|
358
|
+
width: 16,
|
|
359
|
+
byteSize: 2,
|
|
360
|
+
is4state: false,
|
|
361
|
+
direction: "input",
|
|
362
|
+
},
|
|
363
|
+
b: {
|
|
364
|
+
offset: 4,
|
|
365
|
+
width: 16,
|
|
366
|
+
byteSize: 2,
|
|
367
|
+
is4state: false,
|
|
368
|
+
direction: "input",
|
|
369
|
+
},
|
|
370
|
+
sum: {
|
|
371
|
+
offset: 8,
|
|
372
|
+
width: 17,
|
|
373
|
+
byteSize: 4,
|
|
374
|
+
is4state: false,
|
|
375
|
+
direction: "output",
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
const ports: Record<string, PortInfo> = {
|
|
379
|
+
clk: { direction: "input", type: "clock", width: 1 },
|
|
380
|
+
rst: { direction: "input", type: "reset", width: 1 },
|
|
381
|
+
a: { direction: "input", type: "logic", width: 16 },
|
|
382
|
+
b: { direction: "input", type: "logic", width: 16 },
|
|
383
|
+
sum: { direction: "output", type: "logic", width: 17 },
|
|
384
|
+
};
|
|
385
|
+
const handle = mockHandle();
|
|
386
|
+
// Simulate evalComb by writing result into buffer
|
|
387
|
+
(handle.evalComb as ReturnType<typeof vi.fn>).mockImplementation(() => {
|
|
388
|
+
const view = new DataView(buffer);
|
|
389
|
+
const a = view.getUint16(2, true);
|
|
390
|
+
const b = view.getUint16(4, true);
|
|
391
|
+
view.setUint32(8, a + b, true);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const state: DirtyState = { dirty: false };
|
|
395
|
+
const dut = createDut<{
|
|
396
|
+
rst: bigint;
|
|
397
|
+
a: bigint;
|
|
398
|
+
b: bigint;
|
|
399
|
+
readonly sum: bigint;
|
|
400
|
+
}>(buffer, layout, ports, handle, state);
|
|
401
|
+
|
|
402
|
+
dut.a = 100n;
|
|
403
|
+
dut.b = 200n;
|
|
404
|
+
// sum read triggers evalComb
|
|
405
|
+
expect(dut.sum).toBe(300n);
|
|
406
|
+
expect(handle.evalComb).toHaveBeenCalledTimes(1);
|
|
407
|
+
|
|
408
|
+
// second read without changes → no evalComb
|
|
409
|
+
expect(dut.sum).toBe(300n);
|
|
410
|
+
expect(handle.evalComb).toHaveBeenCalledTimes(1);
|
|
411
|
+
});
|
|
300
412
|
});
|
|
301
413
|
|
|
302
414
|
// ---------------------------------------------------------------------------
|
|
@@ -304,145 +416,193 @@ describe("createDut — multiple signals", () => {
|
|
|
304
416
|
// ---------------------------------------------------------------------------
|
|
305
417
|
|
|
306
418
|
describe("createDut — 4-state", () => {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
419
|
+
test("write X to a 4-state signal", () => {
|
|
420
|
+
// 8-bit signal: 1 byte value + 1 byte mask = 2 bytes
|
|
421
|
+
const buffer = makeBuffer(64);
|
|
422
|
+
const layout: Record<string, SignalLayout> = {
|
|
423
|
+
a: {
|
|
424
|
+
offset: 0,
|
|
425
|
+
width: 8,
|
|
426
|
+
byteSize: 1,
|
|
427
|
+
is4state: true,
|
|
428
|
+
direction: "input",
|
|
429
|
+
},
|
|
430
|
+
};
|
|
431
|
+
const ports: Record<string, PortInfo> = {
|
|
432
|
+
a: { direction: "input", type: "logic", width: 8, is4state: true },
|
|
433
|
+
};
|
|
434
|
+
const handle = mockHandle();
|
|
435
|
+
const state: DirtyState = { dirty: false };
|
|
436
|
+
|
|
437
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
438
|
+
|
|
439
|
+
(dut as any).a = X;
|
|
440
|
+
// Value should be 0, mask should be 0xFF
|
|
441
|
+
const [value, mask] = readFourState(buffer, layout.a);
|
|
442
|
+
expect(value).toBe(0n);
|
|
443
|
+
expect(mask).toBe(0xffn);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test("write FourState to a 4-state signal", () => {
|
|
447
|
+
const buffer = makeBuffer(64);
|
|
448
|
+
const layout: Record<string, SignalLayout> = {
|
|
449
|
+
a: {
|
|
450
|
+
offset: 0,
|
|
451
|
+
width: 8,
|
|
452
|
+
byteSize: 1,
|
|
453
|
+
is4state: true,
|
|
454
|
+
direction: "input",
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
const ports: Record<string, PortInfo> = {
|
|
458
|
+
a: { direction: "input", type: "logic", width: 8, is4state: true },
|
|
459
|
+
};
|
|
460
|
+
const handle = mockHandle();
|
|
461
|
+
const state: DirtyState = { dirty: false };
|
|
462
|
+
|
|
463
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
464
|
+
|
|
465
|
+
(dut as any).a = FourState(0b1010, 0b0100);
|
|
466
|
+
const [value, mask] = readFourState(buffer, layout.a);
|
|
467
|
+
expect(value).toBe(0b1010n);
|
|
468
|
+
expect(mask).toBe(0b0100n);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test("writing X to non-4-state signal throws", () => {
|
|
472
|
+
const buffer = makeBuffer(64);
|
|
473
|
+
const layout: Record<string, SignalLayout> = {
|
|
474
|
+
a: {
|
|
475
|
+
offset: 0,
|
|
476
|
+
width: 8,
|
|
477
|
+
byteSize: 1,
|
|
478
|
+
is4state: false,
|
|
479
|
+
direction: "input",
|
|
480
|
+
},
|
|
481
|
+
};
|
|
482
|
+
const ports: Record<string, PortInfo> = {
|
|
483
|
+
a: { direction: "input", type: "logic", width: 8 },
|
|
484
|
+
};
|
|
485
|
+
const handle = mockHandle();
|
|
486
|
+
const state: DirtyState = { dirty: false };
|
|
487
|
+
|
|
488
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
489
|
+
|
|
490
|
+
expect(() => {
|
|
491
|
+
(dut as any).a = X;
|
|
492
|
+
}).toThrow("not 4-state");
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
test("writing FourState to non-4-state signal throws", () => {
|
|
496
|
+
const buffer = makeBuffer(64);
|
|
497
|
+
const layout: Record<string, SignalLayout> = {
|
|
498
|
+
a: {
|
|
499
|
+
offset: 0,
|
|
500
|
+
width: 8,
|
|
501
|
+
byteSize: 1,
|
|
502
|
+
is4state: false,
|
|
503
|
+
direction: "input",
|
|
504
|
+
},
|
|
505
|
+
};
|
|
506
|
+
const ports: Record<string, PortInfo> = {
|
|
507
|
+
a: { direction: "input", type: "logic", width: 8 },
|
|
508
|
+
};
|
|
509
|
+
const handle = mockHandle();
|
|
510
|
+
const state: DirtyState = { dirty: false };
|
|
511
|
+
|
|
512
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
513
|
+
|
|
514
|
+
expect(() => {
|
|
515
|
+
(dut as any).a = FourState(0xa5, 0x0f);
|
|
516
|
+
}).toThrow("not 4-state");
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
test("writing defined value to 4-state signal clears mask", () => {
|
|
520
|
+
const buffer = makeBuffer(64);
|
|
521
|
+
const layout: Record<string, SignalLayout> = {
|
|
522
|
+
a: {
|
|
523
|
+
offset: 0,
|
|
524
|
+
width: 8,
|
|
525
|
+
byteSize: 1,
|
|
526
|
+
is4state: true,
|
|
527
|
+
direction: "input",
|
|
528
|
+
},
|
|
529
|
+
};
|
|
530
|
+
const ports: Record<string, PortInfo> = {
|
|
531
|
+
a: { direction: "input", type: "logic", width: 8, is4state: true },
|
|
532
|
+
};
|
|
533
|
+
const handle = mockHandle();
|
|
534
|
+
const state: DirtyState = { dirty: false };
|
|
535
|
+
|
|
536
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
537
|
+
|
|
538
|
+
// First write X
|
|
539
|
+
(dut as any).a = X;
|
|
540
|
+
const [, maskBefore] = readFourState(buffer, layout.a);
|
|
541
|
+
expect(maskBefore).toBe(0xffn);
|
|
542
|
+
|
|
543
|
+
// Then write a defined value — mask should clear
|
|
544
|
+
dut.a = 42n;
|
|
545
|
+
const [value, maskAfter] = readFourState(buffer, layout.a);
|
|
546
|
+
expect(value).toBe(42n);
|
|
547
|
+
expect(maskAfter).toBe(0n);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
test("reading 4-state output returns value part only", () => {
|
|
551
|
+
const buffer = makeBuffer(64);
|
|
552
|
+
const view = new DataView(buffer);
|
|
553
|
+
const layout: Record<string, SignalLayout> = {
|
|
554
|
+
y: {
|
|
555
|
+
offset: 0,
|
|
556
|
+
width: 8,
|
|
557
|
+
byteSize: 1,
|
|
558
|
+
is4state: true,
|
|
559
|
+
direction: "output",
|
|
560
|
+
},
|
|
561
|
+
};
|
|
562
|
+
const ports: Record<string, PortInfo> = {
|
|
563
|
+
y: { direction: "output", type: "logic", width: 8, is4state: true },
|
|
564
|
+
};
|
|
565
|
+
const handle = mockHandle();
|
|
566
|
+
const state: DirtyState = { dirty: false };
|
|
567
|
+
|
|
568
|
+
const dut = createDut<{ readonly y: bigint }>(
|
|
569
|
+
buffer,
|
|
570
|
+
layout,
|
|
571
|
+
ports,
|
|
572
|
+
handle,
|
|
573
|
+
state,
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
// Set value=0xAB, mask=0x0F (lower 4 bits are X)
|
|
577
|
+
view.setUint8(0, 0xab);
|
|
578
|
+
view.setUint8(1, 0x0f);
|
|
579
|
+
|
|
580
|
+
// DUT getter returns the value part
|
|
581
|
+
expect(dut.y).toBe(0xabn);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
test("write X sets dirty flag", () => {
|
|
585
|
+
const buffer = makeBuffer(64);
|
|
586
|
+
const layout: Record<string, SignalLayout> = {
|
|
587
|
+
a: {
|
|
588
|
+
offset: 0,
|
|
589
|
+
width: 8,
|
|
590
|
+
byteSize: 1,
|
|
591
|
+
is4state: true,
|
|
592
|
+
direction: "input",
|
|
593
|
+
},
|
|
594
|
+
};
|
|
595
|
+
const ports: Record<string, PortInfo> = {
|
|
596
|
+
a: { direction: "input", type: "logic", width: 8, is4state: true },
|
|
597
|
+
};
|
|
598
|
+
const handle = mockHandle();
|
|
599
|
+
const state: DirtyState = { dirty: false };
|
|
600
|
+
|
|
601
|
+
const dut = createDut<{ a: bigint }>(buffer, layout, ports, handle, state);
|
|
602
|
+
|
|
603
|
+
(dut as any).a = X;
|
|
604
|
+
expect(state.dirty).toBe(true);
|
|
605
|
+
});
|
|
446
606
|
});
|
|
447
607
|
|
|
448
608
|
// ---------------------------------------------------------------------------
|
|
@@ -450,220 +610,290 @@ describe("createDut — 4-state", () => {
|
|
|
450
610
|
// ---------------------------------------------------------------------------
|
|
451
611
|
|
|
452
612
|
describe("createDut — array ports", () => {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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
|
-
|
|
613
|
+
test("read/write array elements", () => {
|
|
614
|
+
// 4 elements of 8 bits each = 4 bytes
|
|
615
|
+
const buffer = makeBuffer(64);
|
|
616
|
+
const layout: Record<string, SignalLayout> = {
|
|
617
|
+
data: {
|
|
618
|
+
offset: 0,
|
|
619
|
+
width: 8,
|
|
620
|
+
byteSize: 4,
|
|
621
|
+
is4state: false,
|
|
622
|
+
direction: "input",
|
|
623
|
+
},
|
|
624
|
+
};
|
|
625
|
+
const ports: Record<string, PortInfo> = {
|
|
626
|
+
data: { direction: "input", type: "logic", width: 8, arrayDims: [4] },
|
|
627
|
+
};
|
|
628
|
+
const handle = mockHandle();
|
|
629
|
+
const state: DirtyState = { dirty: false };
|
|
630
|
+
|
|
631
|
+
const dut = createDut<{
|
|
632
|
+
data: {
|
|
633
|
+
at(i: number): bigint;
|
|
634
|
+
set(i: number, v: bigint): void;
|
|
635
|
+
length: number;
|
|
636
|
+
};
|
|
637
|
+
}>(buffer, layout, ports, handle, state);
|
|
638
|
+
|
|
639
|
+
dut.data.set(0, 0xaan);
|
|
640
|
+
dut.data.set(1, 0xbbn);
|
|
641
|
+
dut.data.set(2, 0xccn);
|
|
642
|
+
dut.data.set(3, 0xddn);
|
|
643
|
+
|
|
644
|
+
expect(dut.data.at(0)).toBe(0xaan);
|
|
645
|
+
expect(dut.data.at(1)).toBe(0xbbn);
|
|
646
|
+
expect(dut.data.at(2)).toBe(0xccn);
|
|
647
|
+
expect(dut.data.at(3)).toBe(0xddn);
|
|
648
|
+
expect(dut.data.length).toBe(4);
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
test("1-bit elements (logic [N]): bit-packed read/write", () => {
|
|
652
|
+
// logic[4]: 4 elements of 1 bit each = 1 byte total in native memory
|
|
653
|
+
const buffer = makeBuffer(64);
|
|
654
|
+
const layout: Record<string, SignalLayout> = {
|
|
655
|
+
push: {
|
|
656
|
+
offset: 0,
|
|
657
|
+
width: 1,
|
|
658
|
+
byteSize: 1,
|
|
659
|
+
is4state: false,
|
|
660
|
+
direction: "input",
|
|
661
|
+
},
|
|
662
|
+
};
|
|
663
|
+
const ports: Record<string, PortInfo> = {
|
|
664
|
+
push: { direction: "input", type: "logic", width: 1, arrayDims: [4] },
|
|
665
|
+
};
|
|
666
|
+
const handle = mockHandle();
|
|
667
|
+
const state: DirtyState = { dirty: false };
|
|
668
|
+
|
|
669
|
+
const dut = createDut<{
|
|
670
|
+
push: {
|
|
671
|
+
at(i: number): bigint;
|
|
672
|
+
set(i: number, v: bigint): void;
|
|
673
|
+
length: number;
|
|
674
|
+
};
|
|
675
|
+
}>(buffer, layout, ports, handle, state);
|
|
676
|
+
|
|
677
|
+
dut.push.set(0, 1n);
|
|
678
|
+
dut.push.set(1, 1n);
|
|
679
|
+
dut.push.set(2, 0n);
|
|
680
|
+
dut.push.set(3, 1n);
|
|
681
|
+
|
|
682
|
+
expect(dut.push.at(0)).toBe(1n);
|
|
683
|
+
expect(dut.push.at(1)).toBe(1n);
|
|
684
|
+
expect(dut.push.at(2)).toBe(0n);
|
|
685
|
+
expect(dut.push.at(3)).toBe(1n);
|
|
686
|
+
expect(dut.push.length).toBe(4);
|
|
687
|
+
|
|
688
|
+
// Verify native layout: all 4 elements packed in byte 0
|
|
689
|
+
const view = new DataView(buffer);
|
|
690
|
+
expect(view.getUint8(0)).toBe(0b1011); // bits 0,1,3 set = 0x0B
|
|
691
|
+
expect(view.getUint8(1)).toBe(0); // no overflow into byte 1
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
test("1-bit elements: writes don't corrupt adjacent elements", () => {
|
|
695
|
+
const buffer = makeBuffer(64);
|
|
696
|
+
const layout: Record<string, SignalLayout> = {
|
|
697
|
+
bits: {
|
|
698
|
+
offset: 0,
|
|
699
|
+
width: 1,
|
|
700
|
+
byteSize: 1,
|
|
701
|
+
is4state: false,
|
|
702
|
+
direction: "input",
|
|
703
|
+
},
|
|
704
|
+
};
|
|
705
|
+
const ports: Record<string, PortInfo> = {
|
|
706
|
+
bits: { direction: "input", type: "logic", width: 1, arrayDims: [8] },
|
|
707
|
+
};
|
|
708
|
+
const handle = mockHandle();
|
|
709
|
+
const state: DirtyState = { dirty: false };
|
|
710
|
+
|
|
711
|
+
const dut = createDut<{
|
|
712
|
+
bits: { at(i: number): bigint; set(i: number, v: bigint): void };
|
|
713
|
+
}>(buffer, layout, ports, handle, state);
|
|
714
|
+
|
|
715
|
+
// Set element 0; verify element 1 unaffected
|
|
716
|
+
dut.bits.set(0, 1n);
|
|
717
|
+
expect(dut.bits.at(0)).toBe(1n);
|
|
718
|
+
dut.bits.set(1, 1n);
|
|
719
|
+
expect(dut.bits.at(0)).toBe(1n); // element 0 must not be corrupted
|
|
720
|
+
expect(dut.bits.at(1)).toBe(1n);
|
|
721
|
+
|
|
722
|
+
// Clear element 0; element 1 must remain set
|
|
723
|
+
dut.bits.set(0, 0n);
|
|
724
|
+
expect(dut.bits.at(0)).toBe(0n);
|
|
725
|
+
expect(dut.bits.at(1)).toBe(1n);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
test("1-bit elements: 8 elements span exactly one byte", () => {
|
|
729
|
+
const buffer = makeBuffer(64);
|
|
730
|
+
const layout: Record<string, SignalLayout> = {
|
|
731
|
+
bits: {
|
|
732
|
+
offset: 2,
|
|
733
|
+
width: 1,
|
|
734
|
+
byteSize: 1,
|
|
735
|
+
is4state: false,
|
|
736
|
+
direction: "input",
|
|
737
|
+
},
|
|
738
|
+
};
|
|
739
|
+
const ports: Record<string, PortInfo> = {
|
|
740
|
+
bits: { direction: "input", type: "logic", width: 1, arrayDims: [8] },
|
|
741
|
+
};
|
|
742
|
+
const handle = mockHandle();
|
|
743
|
+
const state: DirtyState = { dirty: false };
|
|
744
|
+
|
|
745
|
+
const dut = createDut<{
|
|
746
|
+
bits: { at(i: number): bigint; set(i: number, v: bigint): void };
|
|
747
|
+
}>(buffer, layout, ports, handle, state);
|
|
748
|
+
|
|
749
|
+
// Set all 8 bits
|
|
750
|
+
for (let i = 0; i < 8; i++) dut.bits.set(i, 1n);
|
|
751
|
+
const view = new DataView(buffer);
|
|
752
|
+
expect(view.getUint8(2)).toBe(0xff);
|
|
753
|
+
|
|
754
|
+
// Clear alternating bits
|
|
755
|
+
for (let i = 0; i < 8; i += 2) dut.bits.set(i, 0n);
|
|
756
|
+
expect(view.getUint8(2)).toBe(0b10101010);
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
test("elementWidth=3: elements spanning byte boundaries read/write correctly", () => {
|
|
760
|
+
// logic<3>[6]: 6 × 3 = 18 bits = 3 bytes
|
|
761
|
+
// Element 2: bitStart=6, spans bytes 0→1 ← cross-byte write/read path
|
|
762
|
+
// Element 5: bitStart=15, spans bytes 1→2 ← cross-byte write/read path
|
|
763
|
+
const buffer = makeBuffer(64);
|
|
764
|
+
const layout: Record<string, SignalLayout> = {
|
|
765
|
+
data: {
|
|
766
|
+
offset: 0,
|
|
767
|
+
width: 3,
|
|
768
|
+
byteSize: 1,
|
|
769
|
+
is4state: false,
|
|
770
|
+
direction: "input",
|
|
771
|
+
},
|
|
772
|
+
};
|
|
773
|
+
const ports: Record<string, PortInfo> = {
|
|
774
|
+
data: { direction: "input", type: "logic", width: 3, arrayDims: [6] },
|
|
775
|
+
};
|
|
776
|
+
const handle = mockHandle();
|
|
777
|
+
const state: DirtyState = { dirty: false };
|
|
778
|
+
|
|
779
|
+
const dut = createDut<{
|
|
780
|
+
data: {
|
|
781
|
+
at(i: number): bigint;
|
|
782
|
+
set(i: number, v: bigint): void;
|
|
783
|
+
length: number;
|
|
784
|
+
};
|
|
785
|
+
}>(buffer, layout, ports, handle, state);
|
|
786
|
+
|
|
787
|
+
// Set all 6 elements to distinct values 1..6
|
|
788
|
+
for (let i = 0; i < 6; i++) dut.data.set(i, BigInt(i + 1));
|
|
789
|
+
for (let i = 0; i < 6; i++) expect(dut.data.at(i)).toBe(BigInt(i + 1));
|
|
790
|
+
|
|
791
|
+
// Overwrite the two cross-byte elements and verify neighbours are unaffected
|
|
792
|
+
dut.data.set(2, 0b101n); // bitStart=6, crosses byte 0→1
|
|
793
|
+
expect(dut.data.at(2)).toBe(5n);
|
|
794
|
+
expect(dut.data.at(1)).toBe(2n); // neighbour below
|
|
795
|
+
expect(dut.data.at(3)).toBe(4n); // neighbour above
|
|
796
|
+
|
|
797
|
+
dut.data.set(5, 0b110n); // bitStart=15, crosses byte 1→2
|
|
798
|
+
expect(dut.data.at(5)).toBe(6n);
|
|
799
|
+
expect(dut.data.at(4)).toBe(5n); // neighbour below
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
test("1-bit 4-state: X writes value=0 and mask=1 at correct byte offset", () => {
|
|
803
|
+
// logic[8] with 4-state: value in byte 0 (bits 0-7 = elements 0-7),
|
|
804
|
+
// mask in byte 1 (bits 0-7 = masks for elements 0-7).
|
|
805
|
+
// totalValueBytes = ceil(8*1/8) = 1, so maskBase = offset + 1.
|
|
806
|
+
const buffer = makeBuffer(64);
|
|
807
|
+
const layout: Record<string, SignalLayout> = {
|
|
808
|
+
bits: {
|
|
809
|
+
offset: 0,
|
|
810
|
+
width: 1,
|
|
811
|
+
byteSize: 1,
|
|
812
|
+
is4state: true,
|
|
813
|
+
direction: "input",
|
|
814
|
+
},
|
|
815
|
+
};
|
|
816
|
+
const ports: Record<string, PortInfo> = {
|
|
817
|
+
bits: {
|
|
818
|
+
direction: "input",
|
|
819
|
+
type: "logic",
|
|
820
|
+
width: 1,
|
|
821
|
+
arrayDims: [8],
|
|
822
|
+
is4state: true,
|
|
823
|
+
},
|
|
824
|
+
};
|
|
825
|
+
const handle = mockHandle();
|
|
826
|
+
const state: DirtyState = { dirty: false };
|
|
827
|
+
|
|
828
|
+
const dut = createDut<{
|
|
829
|
+
bits: { at(i: number): bigint; set(i: number, v: unknown): void };
|
|
830
|
+
}>(buffer, layout, ports, handle, state);
|
|
831
|
+
|
|
832
|
+
const view = new DataView(buffer);
|
|
833
|
+
|
|
834
|
+
// Assign X to element 3: value bit 3 → 0, mask bit 3 → 1
|
|
835
|
+
dut.bits.set(3, X);
|
|
836
|
+
expect(view.getUint8(0) & 0b00001000).toBe(0); // value: bit 3 = 0
|
|
837
|
+
expect(view.getUint8(1) & 0b00001000).toBe(0b00001000); // mask: bit 3 = 1
|
|
838
|
+
|
|
839
|
+
// Write a defined 1 to element 3: value bit 3 → 1, mask bit 3 → 0
|
|
840
|
+
dut.bits.set(3, 1n);
|
|
841
|
+
expect(view.getUint8(0) & 0b00001000).toBe(0b00001000); // value: bit 3 = 1
|
|
842
|
+
expect(view.getUint8(1) & 0b00001000).toBe(0); // mask: bit 3 cleared
|
|
843
|
+
|
|
844
|
+
// Setting elements 0 and 7 to X should only affect those mask bits
|
|
845
|
+
dut.bits.set(0, X);
|
|
846
|
+
dut.bits.set(7, X);
|
|
847
|
+
expect(view.getUint8(1)).toBe(0b10000001); // mask: bits 0 and 7 set
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
test("3-bit 4-state: mask region starts at ceil(totalBits/8) bytes after value", () => {
|
|
851
|
+
// logic<3>[5]: 5 × 3 = 15 bits → totalValueBytes = ceil(15/8) = 2
|
|
852
|
+
// value occupies bytes 0-1, mask occupies bytes 2-3; maskBase = offset + 2
|
|
853
|
+
const buffer = makeBuffer(64);
|
|
854
|
+
const layout: Record<string, SignalLayout> = {
|
|
855
|
+
data: {
|
|
856
|
+
offset: 0,
|
|
857
|
+
width: 3,
|
|
858
|
+
byteSize: 1,
|
|
859
|
+
is4state: true,
|
|
860
|
+
direction: "input",
|
|
861
|
+
},
|
|
862
|
+
};
|
|
863
|
+
const ports: Record<string, PortInfo> = {
|
|
864
|
+
data: {
|
|
865
|
+
direction: "input",
|
|
866
|
+
type: "logic",
|
|
867
|
+
width: 3,
|
|
868
|
+
arrayDims: [5],
|
|
869
|
+
is4state: true,
|
|
870
|
+
},
|
|
871
|
+
};
|
|
872
|
+
const handle = mockHandle();
|
|
873
|
+
const state: DirtyState = { dirty: false };
|
|
874
|
+
|
|
875
|
+
const dut = createDut<{ data: { set(i: number, v: unknown): void } }>(
|
|
876
|
+
buffer,
|
|
877
|
+
layout,
|
|
878
|
+
ports,
|
|
879
|
+
handle,
|
|
880
|
+
state,
|
|
881
|
+
);
|
|
882
|
+
|
|
883
|
+
const view = new DataView(buffer);
|
|
884
|
+
|
|
885
|
+
// Assign X to element 0 (bitStart=0, no cross-byte): value bits 0-2 = 0, mask bits 0-2 = 0b111
|
|
886
|
+
dut.data.set(0, X);
|
|
887
|
+
expect(view.getUint8(0) & 0b111).toBe(0); // value byte 0: bits 0-2 cleared
|
|
888
|
+
expect(view.getUint8(2) & 0b111).toBe(0b111); // mask byte 2: bits 0-2 set
|
|
889
|
+
expect(view.getUint8(1)).toBe(0); // value byte 1: untouched
|
|
890
|
+
expect(view.getUint8(3)).toBe(0); // mask byte 3: untouched
|
|
891
|
+
|
|
892
|
+
// Write a defined value to element 0, verify mask cleared
|
|
893
|
+
dut.data.set(0, 0b110n);
|
|
894
|
+
expect(view.getUint8(0) & 0b111).toBe(0b110); // value bits 0-2 = 6
|
|
895
|
+
expect(view.getUint8(2) & 0b111).toBe(0); // mask bits 0-2 cleared
|
|
896
|
+
});
|
|
667
897
|
});
|
|
668
898
|
|
|
669
899
|
// ---------------------------------------------------------------------------
|
|
@@ -671,135 +901,200 @@ describe("createDut — array ports", () => {
|
|
|
671
901
|
// ---------------------------------------------------------------------------
|
|
672
902
|
|
|
673
903
|
describe("createDut — interface ports with array members", () => {
|
|
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
|
-
|
|
904
|
+
test("interface member with arrayDims uses array accessor", () => {
|
|
905
|
+
// Interface array pattern: bus: modport Bus::consumer [2]
|
|
906
|
+
// Veryl expands to bus.data with arrayDims:[2], bus.valid with arrayDims:[2]
|
|
907
|
+
const buffer = makeBuffer(64);
|
|
908
|
+
const layout: Record<string, SignalLayout> = {
|
|
909
|
+
"bus.data": {
|
|
910
|
+
offset: 0,
|
|
911
|
+
width: 8,
|
|
912
|
+
byteSize: 2,
|
|
913
|
+
is4state: false,
|
|
914
|
+
direction: "input",
|
|
915
|
+
},
|
|
916
|
+
"bus.valid": {
|
|
917
|
+
offset: 2,
|
|
918
|
+
width: 1,
|
|
919
|
+
byteSize: 1,
|
|
920
|
+
is4state: false,
|
|
921
|
+
direction: "input",
|
|
922
|
+
},
|
|
923
|
+
};
|
|
924
|
+
const ports: Record<string, PortInfo> = {
|
|
925
|
+
bus: {
|
|
926
|
+
direction: "inout",
|
|
927
|
+
type: "logic",
|
|
928
|
+
width: 0,
|
|
929
|
+
interface: {
|
|
930
|
+
data: { direction: "input", type: "logic", width: 8, arrayDims: [2] },
|
|
931
|
+
valid: {
|
|
932
|
+
direction: "input",
|
|
933
|
+
type: "logic",
|
|
934
|
+
width: 1,
|
|
935
|
+
arrayDims: [2],
|
|
936
|
+
},
|
|
937
|
+
},
|
|
938
|
+
},
|
|
939
|
+
};
|
|
940
|
+
const handle = mockHandle();
|
|
941
|
+
const state: DirtyState = { dirty: false };
|
|
942
|
+
|
|
943
|
+
const dut = createDut<{
|
|
944
|
+
bus: {
|
|
945
|
+
data: {
|
|
946
|
+
at(i: number): bigint;
|
|
947
|
+
set(i: number, v: bigint): void;
|
|
948
|
+
length: number;
|
|
949
|
+
};
|
|
950
|
+
valid: {
|
|
951
|
+
at(i: number): bigint;
|
|
952
|
+
set(i: number, v: bigint): void;
|
|
953
|
+
length: number;
|
|
954
|
+
};
|
|
955
|
+
};
|
|
956
|
+
}>(buffer, layout, ports, handle, state);
|
|
957
|
+
|
|
958
|
+
// data: 2 elements of 8 bits each
|
|
959
|
+
dut.bus.data.set(0, 0xaan);
|
|
960
|
+
dut.bus.data.set(1, 0xbbn);
|
|
961
|
+
expect(dut.bus.data.at(0)).toBe(0xaan);
|
|
962
|
+
expect(dut.bus.data.at(1)).toBe(0xbbn);
|
|
963
|
+
expect(dut.bus.data.length).toBe(2);
|
|
964
|
+
|
|
965
|
+
// valid: 2 elements of 1 bit each (bit-packed)
|
|
966
|
+
dut.bus.valid.set(0, 1n);
|
|
967
|
+
dut.bus.valid.set(1, 0n);
|
|
968
|
+
expect(dut.bus.valid.at(0)).toBe(1n);
|
|
969
|
+
expect(dut.bus.valid.at(1)).toBe(0n);
|
|
970
|
+
expect(dut.bus.valid.length).toBe(2);
|
|
971
|
+
});
|
|
972
|
+
|
|
973
|
+
test("interface with mixed scalar and array members", () => {
|
|
974
|
+
const buffer = makeBuffer(64);
|
|
975
|
+
const layout: Record<string, SignalLayout> = {
|
|
976
|
+
"bus.data": {
|
|
977
|
+
offset: 0,
|
|
978
|
+
width: 8,
|
|
979
|
+
byteSize: 2,
|
|
980
|
+
is4state: false,
|
|
981
|
+
direction: "input",
|
|
982
|
+
},
|
|
983
|
+
"bus.valid": {
|
|
984
|
+
offset: 4,
|
|
985
|
+
width: 1,
|
|
986
|
+
byteSize: 1,
|
|
987
|
+
is4state: false,
|
|
988
|
+
direction: "input",
|
|
989
|
+
},
|
|
990
|
+
};
|
|
991
|
+
const ports: Record<string, PortInfo> = {
|
|
992
|
+
bus: {
|
|
993
|
+
direction: "inout",
|
|
994
|
+
type: "logic",
|
|
995
|
+
width: 0,
|
|
996
|
+
interface: {
|
|
997
|
+
data: { direction: "input", type: "logic", width: 8, arrayDims: [2] },
|
|
998
|
+
valid: { direction: "input", type: "logic", width: 1 },
|
|
999
|
+
},
|
|
1000
|
+
},
|
|
1001
|
+
};
|
|
1002
|
+
const handle = mockHandle();
|
|
1003
|
+
const state: DirtyState = { dirty: false };
|
|
1004
|
+
|
|
1005
|
+
const dut = createDut<{
|
|
1006
|
+
bus: {
|
|
1007
|
+
data: {
|
|
1008
|
+
at(i: number): bigint;
|
|
1009
|
+
set(i: number, v: bigint): void;
|
|
1010
|
+
length: number;
|
|
1011
|
+
};
|
|
1012
|
+
valid: bigint;
|
|
1013
|
+
};
|
|
1014
|
+
}>(buffer, layout, ports, handle, state);
|
|
1015
|
+
|
|
1016
|
+
// Array member
|
|
1017
|
+
dut.bus.data.set(0, 0x11n);
|
|
1018
|
+
dut.bus.data.set(1, 0x22n);
|
|
1019
|
+
expect(dut.bus.data.at(0)).toBe(0x11n);
|
|
1020
|
+
expect(dut.bus.data.at(1)).toBe(0x22n);
|
|
1021
|
+
|
|
1022
|
+
// Scalar member
|
|
1023
|
+
dut.bus.valid = 1n;
|
|
1024
|
+
expect(dut.bus.valid).toBe(1n);
|
|
1025
|
+
});
|
|
755
1026
|
});
|
|
756
1027
|
|
|
757
1028
|
describe("createDut — interface ports", () => {
|
|
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
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
1029
|
+
test("nested interface members", () => {
|
|
1030
|
+
const buffer = makeBuffer(64);
|
|
1031
|
+
const layout: Record<string, SignalLayout> = {
|
|
1032
|
+
"bus.addr": {
|
|
1033
|
+
offset: 0,
|
|
1034
|
+
width: 32,
|
|
1035
|
+
byteSize: 4,
|
|
1036
|
+
is4state: false,
|
|
1037
|
+
direction: "input",
|
|
1038
|
+
},
|
|
1039
|
+
"bus.data": {
|
|
1040
|
+
offset: 4,
|
|
1041
|
+
width: 32,
|
|
1042
|
+
byteSize: 4,
|
|
1043
|
+
is4state: false,
|
|
1044
|
+
direction: "input",
|
|
1045
|
+
},
|
|
1046
|
+
"bus.valid": {
|
|
1047
|
+
offset: 8,
|
|
1048
|
+
width: 1,
|
|
1049
|
+
byteSize: 1,
|
|
1050
|
+
is4state: false,
|
|
1051
|
+
direction: "input",
|
|
1052
|
+
},
|
|
1053
|
+
"bus.ready": {
|
|
1054
|
+
offset: 9,
|
|
1055
|
+
width: 1,
|
|
1056
|
+
byteSize: 1,
|
|
1057
|
+
is4state: false,
|
|
1058
|
+
direction: "output",
|
|
1059
|
+
},
|
|
1060
|
+
};
|
|
1061
|
+
const ports: Record<string, PortInfo> = {
|
|
1062
|
+
bus: {
|
|
1063
|
+
direction: "input",
|
|
1064
|
+
type: "logic",
|
|
1065
|
+
width: 0,
|
|
1066
|
+
interface: {
|
|
1067
|
+
addr: { direction: "input", type: "logic", width: 32 },
|
|
1068
|
+
data: { direction: "input", type: "logic", width: 32 },
|
|
1069
|
+
valid: { direction: "input", type: "logic", width: 1 },
|
|
1070
|
+
ready: { direction: "output", type: "logic", width: 1 },
|
|
1071
|
+
},
|
|
1072
|
+
},
|
|
1073
|
+
};
|
|
1074
|
+
const handle = mockHandle();
|
|
1075
|
+
(handle.evalComb as ReturnType<typeof vi.fn>).mockImplementation(() => {
|
|
1076
|
+
const view = new DataView(buffer);
|
|
1077
|
+
// mock: ready = valid
|
|
1078
|
+
view.setUint8(9, view.getUint8(8));
|
|
1079
|
+
});
|
|
1080
|
+
|
|
1081
|
+
const state: DirtyState = { dirty: false };
|
|
1082
|
+
const dut = createDut<{
|
|
1083
|
+
bus: {
|
|
1084
|
+
addr: bigint;
|
|
1085
|
+
data: bigint;
|
|
1086
|
+
valid: bigint;
|
|
1087
|
+
readonly ready: bigint;
|
|
1088
|
+
};
|
|
1089
|
+
}>(buffer, layout, ports, handle, state);
|
|
1090
|
+
|
|
1091
|
+
dut.bus.addr = 0x1000n;
|
|
1092
|
+
dut.bus.data = 0xffn;
|
|
1093
|
+
dut.bus.valid = 1n;
|
|
1094
|
+
|
|
1095
|
+
expect(dut.bus.addr).toBe(0x1000n);
|
|
1096
|
+
expect(dut.bus.data).toBe(0xffn);
|
|
1097
|
+
expect(dut.bus.ready).toBe(1n);
|
|
1098
|
+
expect(handle.evalComb).toHaveBeenCalledTimes(1);
|
|
1099
|
+
});
|
|
805
1100
|
});
|