Skip to content

Commit f66a290

Browse files
authored
Driving and receiving of hierarchical PairInterfaces (#628)
1 parent 42a4e90 commit f66a290

File tree

3 files changed

+247
-0
lines changed

3 files changed

+247
-0
lines changed

.github/configs/mlc_config.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
},
1515
{
1616
"pattern":"^https://www.nandland.com"
17+
},
18+
{
19+
"pattern":"^https://.*\\.fandom\\.com"
1720
}
1821
]
1922
}

lib/src/interfaces/pair_interface.dart

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,128 @@ class PairInterface extends Interface<PairDirection> {
289289
return subInterface;
290290
}
291291

292+
/// Makes `this` drive interface signals tagged with [tags] on [other].
293+
///
294+
/// In addition to the base [Interface.driveOther] functionality, this also
295+
/// handles driving signals on all [subInterfaces] hierarchically when [other]
296+
/// is a [PairInterface], considering `reverse`.
297+
@override
298+
void driveOther(
299+
Interface<PairDirection> other, Iterable<PairDirection> tags) {
300+
super.driveOther(other, tags);
301+
302+
if (other is PairInterface) {
303+
subInterfaces.forEach((subIntfName, subInterface) {
304+
if (!other.subInterfaces.containsKey(subIntfName)) {
305+
throw InterfaceTypeException(
306+
other, 'missing a sub-interface named $subIntfName');
307+
}
308+
309+
if (_subInterfaces[subIntfName]!.reverse) {
310+
subInterface.receiveOther(other.subInterfaces[subIntfName]!, tags);
311+
} else {
312+
subInterface.driveOther(other.subInterfaces[subIntfName]!, tags);
313+
}
314+
});
315+
}
316+
}
317+
318+
/// Makes `this` signals tagged with [tags] be driven by [other].
319+
///
320+
/// In addition to the base [Interface.receiveOther] functionality, this also
321+
/// handles receiving signals from all [subInterfaces] hierarchically when
322+
/// [other] is a [PairInterface], considering `reverse`.
323+
@override
324+
void receiveOther(
325+
Interface<PairDirection> other, Iterable<PairDirection> tags) {
326+
super.receiveOther(other, tags);
327+
328+
if (other is PairInterface) {
329+
subInterfaces.forEach((subIntfName, subInterface) {
330+
if (!other.subInterfaces.containsKey(subIntfName)) {
331+
throw InterfaceTypeException(
332+
other, 'missing a sub-interface named $subIntfName');
333+
}
334+
335+
if (_subInterfaces[subIntfName]!.reverse) {
336+
subInterface.driveOther(other.subInterfaces[subIntfName]!, tags);
337+
} else {
338+
subInterface.receiveOther(other.subInterfaces[subIntfName]!, tags);
339+
}
340+
});
341+
}
342+
}
343+
344+
/// Makes `this` conditionally drive interface signals tagged with [tags] on
345+
/// [other].
346+
///
347+
/// In addition to the base [Interface.conditionalDriveOther] functionality,
348+
/// this also handles conditional driving of signals on all [subInterfaces]
349+
/// hierarchically when [other] is a [PairInterface]. Returns a
350+
/// [ConditionalGroup] that combines all conditionals from the main interface
351+
/// and sub-interfaces, considering `reverse`.
352+
@override
353+
Conditional conditionalDriveOther(
354+
Interface<PairDirection> other, Iterable<PairDirection> tags) {
355+
final conditionals = <Conditional>[
356+
super.conditionalDriveOther(other, tags)
357+
];
358+
359+
if (other is PairInterface) {
360+
subInterfaces.forEach((subIntfName, subInterface) {
361+
if (!other.subInterfaces.containsKey(subIntfName)) {
362+
throw InterfaceTypeException(
363+
other, 'missing a sub-interface named $subIntfName');
364+
}
365+
366+
if (_subInterfaces[subIntfName]!.reverse) {
367+
conditionals.add(subInterface.conditionalReceiveOther(
368+
other.subInterfaces[subIntfName]!, tags));
369+
} else {
370+
conditionals.add(subInterface.conditionalDriveOther(
371+
other.subInterfaces[subIntfName]!, tags));
372+
}
373+
});
374+
}
375+
376+
return ConditionalGroup(conditionals);
377+
}
378+
379+
/// Makes `this` signals tagged with [tags] be driven conditionally by
380+
/// [other].
381+
///
382+
/// In addition to the base [Interface.conditionalReceiveOther] functionality,
383+
/// this also handles conditional receiving of signals from all
384+
/// [subInterfaces] hierarchically when [other] is a [PairInterface]. Returns
385+
/// a [ConditionalGroup] that combines all conditionals from the main
386+
/// interface and sub-interfaces, considering `reverse`.
387+
@override
388+
Conditional conditionalReceiveOther(
389+
Interface<PairDirection> other, Iterable<PairDirection> tags) {
390+
final conditionals = <Conditional>[
391+
super.conditionalReceiveOther(other, tags)
392+
];
393+
394+
if (other is PairInterface) {
395+
subInterfaces.forEach((subIntfName, subInterface) {
396+
if (!other.subInterfaces.containsKey(subIntfName)) {
397+
throw InterfaceTypeException(
398+
other, 'missing a sub-interface named $subIntfName');
399+
}
400+
401+
if (_subInterfaces[subIntfName]!.reverse) {
402+
conditionals.add(subInterface.conditionalDriveOther(
403+
other.subInterfaces[subIntfName]!, tags));
404+
} else {
405+
conditionals.add(subInterface.conditionalReceiveOther(
406+
other.subInterfaces[subIntfName]!, tags));
407+
}
408+
});
409+
}
410+
411+
return ConditionalGroup(conditionals);
412+
}
413+
292414
@override
293415
@mustBeOverridden
294416
// ignore: deprecated_member_use_from_same_package

test/pair_interface_test.dart

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,42 @@ class SimpleInterface extends PairInterface {
3737
SimpleInterface clone() => SimpleInterface();
3838
}
3939

40+
class SubInterface extends PairInterface {
41+
Logic get subReq => port('sub_req');
42+
Logic get subRsp => port('sub_rsp');
43+
44+
SubInterface()
45+
: super(
46+
portsFromConsumer: [Logic.port('sub_rsp', 8)],
47+
portsFromProvider: [Logic.port('sub_req', 8)],
48+
);
49+
50+
@override
51+
SubInterface clone() => SubInterface();
52+
}
53+
54+
class HierarchicalInterface extends PairInterface {
55+
Logic get mainReq => port('main_req');
56+
Logic get mainRsp => port('main_rsp');
57+
SubInterface get sub1 => subInterfaces['sub1']! as SubInterface;
58+
SubInterface get sub2 => subInterfaces['sub2']! as SubInterface;
59+
60+
HierarchicalInterface({bool excludeSubInterfaces = false})
61+
: super(
62+
portsFromProvider: [Logic.port('main_req', 8)],
63+
portsFromConsumer: [Logic.port('main_rsp', 8)],
64+
) {
65+
if (!excludeSubInterfaces) {
66+
addSubInterface('sub1', SubInterface(), uniquify: (orig) => 'sub1_$orig');
67+
addSubInterface('sub2', SubInterface(),
68+
uniquify: (orig) => 'sub2_$orig', reverse: true);
69+
}
70+
}
71+
72+
@override
73+
HierarchicalInterface clone() => HierarchicalInterface();
74+
}
75+
4076
class SimpleProvider extends Module {
4177
late final SimpleInterface _intf;
4278
SimpleProvider(SimpleInterface intf) {
@@ -118,6 +154,34 @@ class PassthroughPairIntfModule extends Module {
118154
}
119155
}
120156

157+
class SubInterfaceTestModule extends Module {
158+
SubInterfaceTestModule(
159+
HierarchicalInterface intf1, HierarchicalInterface intf2,
160+
{required bool useConditional}) {
161+
intf1 = addPairInterfacePorts(
162+
intf1,
163+
PairRole.consumer,
164+
uniquify: (original) => 'intf1_$original',
165+
);
166+
intf2 = addPairInterfacePorts(
167+
intf2,
168+
PairRole.provider,
169+
uniquify: (original) => 'intf2_$original',
170+
);
171+
172+
if (useConditional) {
173+
Combinational([
174+
intf1.conditionalDriveOther(intf2, {PairDirection.fromProvider}),
175+
intf1.conditionalReceiveOther(intf2, {PairDirection.fromConsumer}),
176+
]);
177+
} else {
178+
intf1
179+
..driveOther(intf2, {PairDirection.fromProvider})
180+
..receiveOther(intf2, {PairDirection.fromConsumer});
181+
}
182+
}
183+
}
184+
121185
void main() {
122186
tearDown(() async {
123187
await Simulator.reset();
@@ -187,4 +251,62 @@ void main() {
187251
}
188252
}
189253
});
254+
255+
group('sub-interface drive and receive other', () {
256+
for (final useConditional in [false, true]) {
257+
test('with useConditional: $useConditional', () async {
258+
final mod = SubInterfaceTestModule(
259+
HierarchicalInterface(),
260+
HierarchicalInterface(),
261+
useConditional: useConditional,
262+
);
263+
await mod.build();
264+
265+
final vectors = [
266+
Vector({
267+
'intf1_main_req': 0x01,
268+
'intf2_main_rsp': 0x12,
269+
'intf1_sub1_sub_req': 0x23,
270+
'intf2_sub1_sub_rsp': 0x34,
271+
'intf1_sub2_sub_rsp': 0x45,
272+
'intf2_sub2_sub_req': 0x56
273+
}, {
274+
'intf2_main_req': 0x01,
275+
'intf1_main_rsp': 0x12,
276+
'intf2_sub1_sub_req': 0x23,
277+
'intf1_sub1_sub_rsp': 0x34,
278+
'intf2_sub2_sub_rsp': 0x45,
279+
'intf1_sub2_sub_req': 0x56
280+
}),
281+
];
282+
283+
await SimCompare.checkFunctionalVector(mod, vectors);
284+
SimCompare.checkIverilogVector(mod, vectors);
285+
});
286+
}
287+
});
288+
289+
test('hierarchical interface creation and access', () {
290+
final intf = HierarchicalInterface();
291+
292+
// Test that sub-interfaces were created properly
293+
expect(intf.subInterfaces.containsKey('sub1'), isTrue);
294+
expect(intf.subInterfaces.containsKey('sub2'), isTrue);
295+
expect(intf.subInterfaces.length, equals(2));
296+
297+
// Test access to sub-interface ports
298+
expect(intf.sub1.subReq.width, equals(8));
299+
expect(intf.sub1.subRsp.width, equals(8));
300+
expect(intf.sub2.subReq.width, equals(8));
301+
expect(intf.sub2.subRsp.width, equals(8));
302+
});
303+
304+
test('sub-interface missing error handling', () {
305+
expect(
306+
() => HierarchicalInterface().driveOther(
307+
HierarchicalInterface(excludeSubInterfaces: true),
308+
{PairDirection.fromProvider}),
309+
throwsA(isA<InterfaceTypeException>()),
310+
);
311+
});
190312
}

0 commit comments

Comments
 (0)