Skip to content

Commit

Permalink
Logic.assignSubset for partial assignments to Logic (#502)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Aug 6, 2024
1 parent 6474134 commit dd3f43d
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 32 deletions.
19 changes: 17 additions & 2 deletions doc/user_guide/_docs/A04-assignment.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,29 @@
title: "Assignment"
permalink: /docs/assignment/
excerpt: "Assignment"
last_modified_at: 2022-12-06
last_modified_at: 2024-08-02
toc: true
---

To assign one signal to the value of another signal, use the `<=` operator. This is a hardware synthesizable assignment connecting two wires together.

```dart
var a = Logic(), b = Logic();
// assign a to always have the same value as b
// assign `a` to always have the same value as `b`
a <= b;
```

It is also possible to do a partial assignment to a signal using `assignSubset`.

```dart
var a = Logic(width: 3), b = Logic(width: 2);
// assign the bottom two bits of `a` to have the same value as `b`
a.assignSubset(b.elements);
// assign the upper bit (index 2) of `a` to be 0
a.assignSubset([Const(0)], 2);
```

If you're assigning groups of bits that are already collected as a single `Logic`, consider using a [`swizzle`](https://intel.github.io/rohd-website/docs/bus-range-swizzling/).
35 changes: 35 additions & 0 deletions lib/src/signals/logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -826,4 +826,39 @@ class Logic {

return selected;
}

/// If [assignSubset] has been used on this signal, a reference to the
/// [LogicArray] that is usd to drive `this`.
LogicArray? _subsetDriver;

/// Performs an assignment operation on a portion this signal to be driven by
/// [updatedSubset]. Each index of [updatedSubset] will be assigned to drive
/// the corresponding index, plus [start], of this signal.
///
/// Each of the elements of [updatedSubset] must have the same [width] as the
/// corresponding member of [elements] of this signal.
///
/// Example:
/// ```dart
/// // assign elements 2 and 3 of receiverLogic to sig1 and sig2, respectively
/// receiverLogic.assignSubset([sig1, sig2], start: 2);
/// ```
void assignSubset(List<Logic> updatedSubset, {int start = 0}) {
if (updatedSubset.length > width - start) {
throw SignalWidthMismatchException.forWidthOverflow(
updatedSubset.length, width - start);
}

if (_subsetDriver == null) {
_subsetDriver = (isNet ? LogicArray.net : LogicArray.new)(
[width],
1,
name: '${name}_subset',
naming: Naming.unnamed,
);
this <= _subsetDriver!;
}

_subsetDriver!.assignSubset(updatedSubset, start: start);
}
}
29 changes: 0 additions & 29 deletions lib/src/signals/logic_array.dart
Original file line number Diff line number Diff line change
Expand Up @@ -234,33 +234,4 @@ class LogicArray extends LogicStructure {
naming: Naming.mergeable,
);
}

/// Perform Assign operation on a Logic subset or slice
///
/// Assigns part of this LogicArray with a given [updatedSubset] of type
/// [List<Logic>]. The update is performed from a given [start] position
/// to the length of the [updatedSubset].
///
/// Example:
/// ```dart
/// LogicArray sampleLogic;
/// // Note: updatedSubset.length < (sampleLogic.length - start)
/// List<Logic> updatedSubset;
/// // Assign part of sampleLogic as [updatedSubset]
/// sampleLogic.assignSubset(updatedSubset); // start = 0 by default
/// // assign updated subset to sampleLogic[10:10+updatedSubset.length]
/// sampleLogic.assignSubset(updatedSubset, 10);
/// ```
///
void assignSubset(List<Logic> updatedSubset, {int start = 0}) {
if (updatedSubset.length > elements.length - start) {
throw SignalWidthMismatchException.forWidthOverflow(
updatedSubset.length, elements.length - start);
}

// Assign Logic array from `start` index to `start+updatedSubset.length`
for (var i = 0; i < updatedSubset.length; i++) {
elements[start + i] <= updatedSubset[i];
}
}
}
19 changes: 19 additions & 0 deletions lib/src/signals/logic_structure.dart
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,19 @@ class LogicStructure implements Logic {
return newWithSet;
}

@override
void assignSubset(List<Logic> updatedSubset, {int start = 0}) {
if (updatedSubset.length > elements.length - start) {
throw SignalWidthMismatchException.forWidthOverflow(
updatedSubset.length, elements.length - start);
}

// Assign Logic array from `start` index to `start+updatedSubset.length`
for (var i = 0; i < updatedSubset.length; i++) {
elements[start + i] <= updatedSubset[i];
}
}

@override
Logic operator ~() => ~packed;

Expand Down Expand Up @@ -586,4 +599,10 @@ class LogicStructure implements Logic {

@override
List<Logic> get _srcConnections => throw UnsupportedError('Unnecessary');

@override
LogicArray? get _subsetDriver => throw UnsupportedError('Unnecessary');

@override
set _subsetDriver(LogicArray? _) => throw UnsupportedError('Unnecessary');
}
125 changes: 124 additions & 1 deletion test/assignment_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021-2023 Intel Corporation
// Copyright (C) 2021-2024 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// assignment_test.dart
Expand All @@ -25,7 +25,58 @@ class ExampleModule extends Module {
Logic get out => output('out');
}

class LogicSubsetModule extends Module {
LogicSubsetModule(int offset, int resultWidth, Logic subset) {
subset = addInput('subset', subset, width: subset.width);

addOutput('result', width: resultWidth)
.assignSubset(subset.elements, start: offset);
}
}

class MyStruct extends LogicStructure {
final Logic smaller;
final Logic big;

factory MyStruct() => MyStruct._(
Logic(name: 'smaller'),
Logic(name: 'big', width: 8),
);

MyStruct._(this.smaller, this.big) : super([smaller, big], name: 'myStruct');

@override
LogicStructure clone({String? name}) => MyStruct();
}

class LogicStructSubsetModule extends Module {
LogicStructSubsetModule(Logic smaller, Logic big) {
smaller = addInput('smaller', smaller);
big = addInput('big', big, width: 8);

final struct = MyStruct()..assignSubset([smaller, big]);

addOutput('result', width: struct.width) <= struct;
}
}

class LogicNetSubsetModule extends Module {
LogicNetSubsetModule(int offset1, int offset2, LogicNet subset1,
LogicNet subset2, LogicNet result) {
subset1 = addInOut('subset1', subset1, width: subset1.width);
subset2 = addInOut('subset2', subset2, width: subset2.width);

result = addInOut('result', result, width: result.width)
..assignSubset(subset1.elements, start: offset1)
..assignSubset(subset2.elements, start: offset2);
}
}

void main() {
tearDown(() async {
await Simulator.reset();
});

// From https://github.com/intel/rohd/issues/159
// Thank you to @chykon for reporting!
test('const comb assignment', () async {
Expand All @@ -43,4 +94,76 @@ void main() {
);
expect(simResult, equals(true));
});

group('assign subset', () {
group('logic', () {
test('single bit', () async {
final mod = LogicSubsetModule(3, 8, Logic());
await mod.build();

final vectors = [
Vector({'subset': bin('1')},
{'result': LogicValue.ofString('zzzz1zzz')}),
];

await SimCompare.checkFunctionalVector(mod, vectors);
SimCompare.checkIverilogVector(mod, vectors);
});

test('multiple bits', () async {
final mod = LogicSubsetModule(2, 8, Logic(width: 4));
await mod.build();

final vectors = [
Vector({'subset': bin('0110')},
{'result': LogicValue.ofString('zz0110zz')}),
];

await SimCompare.checkFunctionalVector(mod, vectors);
SimCompare.checkIverilogVector(mod, vectors);
});

test('width mismatch fails', () {
expect(() => Logic(width: 8).assignSubset([Logic(width: 4)]),
throwsA(isA<SignalWidthMismatchException>()));
});

test('out of bounds fails', () {
expect(() => Logic(width: 8).assignSubset([Logic(), Logic()], start: 7),
throwsA(isA<SignalWidthMismatchException>()));
});
});

test('logic structure', () async {
final mod = LogicStructSubsetModule(Logic(), Logic(width: 8));
await mod.build();

final vectors = [
Vector(
{'smaller': bin('1'), 'big': bin('1010')}, {'result': bin('10101')})
];

await SimCompare.checkFunctionalVector(mod, vectors);
SimCompare.checkIverilogVector(mod, vectors);
});

test('logic net is multi-assignable', () async {
final mod = LogicNetSubsetModule(
2,
4,
LogicNet(width: 4),
LogicNet(width: 4),
LogicNet(width: 8),
);
await mod.build();

final vectors = [
Vector({'subset1': bin('0000'), 'subset2': bin('1111')},
{'result': LogicValue.ofString('11xx00zz')}),
];

await SimCompare.checkFunctionalVector(mod, vectors);
SimCompare.checkIverilogVector(mod, vectors);
});
});
}

0 comments on commit dd3f43d

Please sign in to comment.