Skip to content

Commit

Permalink
Cases (#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
mjayasim9 authored Aug 29, 2023
1 parent b0f17b3 commit e00e350
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 2 deletions.
14 changes: 14 additions & 0 deletions lib/src/exceptions/logic/signal_width_mismatch_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,18 @@ class SignalWidthMismatchException extends RohdException {
: super('Signal ${signal.name} has the wrong width.'
' Expected $expectedWidth but found ${signal.width}.'
' $additionalMessage');

/// Constructs a new [Exception] for when a dynamic has a wrong width.
SignalWidthMismatchException.forDynamic(
dynamic val, int expectedWidth, int actualWidth,
{String additionalMessage = ''})
: super('Value $val has the wrong width.'
' Expected $expectedWidth but found $actualWidth.'
' $additionalMessage');

/// Constructs a new [Exception] for when a dynamic has no width or it could
/// not be inferred.
SignalWidthMismatchException.forNull(dynamic val)
: super('Could not infer width of input $val.'
' Please provide a valid width.');
}
76 changes: 74 additions & 2 deletions lib/src/modules/conditional.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import 'package:meta/meta.dart';
import 'package:rohd/rohd.dart';
import 'package:rohd/src/collections/duplicate_detection_set.dart';
import 'package:rohd/src/collections/traverseable_collection.dart';
import 'package:rohd/src/exceptions/conditionals/conditional_exceptions.dart';
import 'package:rohd/src/exceptions/module/port_width_mismatch_exception.dart';
import 'package:rohd/src/exceptions/exceptions.dart';
import 'package:rohd/src/utilities/sanitizer.dart';
import 'package:rohd/src/utilities/synchronous_propagator.dart';
import 'package:rohd/src/utilities/uniquifier.dart';
Expand Down Expand Up @@ -912,6 +911,79 @@ enum ConditionalType {
priority
}

/// Shorthand for a [Case] inside a [Conditional] block.
///
/// It is used to assign a signal based on a condition with multiple cases to
/// consider. For e.g., this can be used instead of a nested [mux].
///
/// The result is of type [Logic] and it is determined by conditionaly matching
/// the expression with the values of each item in conditions. If width of the
/// input is not provided, then the width of the result is inferred from the
/// width of the entries.
Logic cases(Logic expression, Map<dynamic, dynamic> conditions,
{int? width,
ConditionalType conditionalType = ConditionalType.none,
dynamic defaultValue}) {
for (final conditionValue in [
...conditions.values,
if (defaultValue != null) defaultValue
]) {
int? inferredWidth;

if (conditionValue is Logic) {
inferredWidth = conditionValue.width;
} else if (conditionValue is LogicValue) {
inferredWidth = conditionValue.width;
}

if (width != inferredWidth && width != null && inferredWidth != null) {
throw SignalWidthMismatchException.forDynamic(
conditionValue, width, inferredWidth);
}

width ??= inferredWidth;
}

if (width == null) {
throw SignalWidthMismatchException.forNull(conditions);
}

for (final condition in conditions.entries) {
if (condition.key is Logic) {
if (expression.width != (condition.key as Logic).width) {
throw SignalWidthMismatchException.forDynamic(
condition.key, expression.width, (condition.key as Logic).width);
}
}

if (condition.key is LogicValue) {
if (expression.width != (condition.key as LogicValue).width) {
throw SignalWidthMismatchException.forDynamic(condition.key,
expression.width, (condition.key as LogicValue).width);
}
}
}

final result = Logic(name: 'result', width: width);

Combinational([
Case(
expression,
[
for (final condition in conditions.entries)
CaseItem(
condition.key is Logic
? condition.key as Logic
: Const(condition.key, width: expression.width),
[result < condition.value])
],
conditionalType: conditionalType,
defaultItem: defaultValue != null ? [result < defaultValue] : null)
]);

return result;
}

/// A block of [CaseItem]s where only the one with a matching [CaseItem.value]
/// is executed.
///
Expand Down
156 changes: 156 additions & 0 deletions test/gate_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/// Author: Max Korbel <[email protected]>
///
import 'package:rohd/rohd.dart';
import 'package:rohd/src/exceptions/exceptions.dart';
import 'package:rohd/src/utilities/simcompare.dart';
import 'package:test/test.dart';

Expand Down Expand Up @@ -187,6 +188,161 @@ void main() {
});
});

test('mux shorthand', () {
final control = Logic();
final d0 = Logic();
final d1 = Logic();
final result = mux(control, d1, d0);

d0.put(0);
d1.put(1);
control.put(0);

expect(result.value, LogicValue.zero);

control.put(1);

expect(result.value, LogicValue.one);
});

group('Cases', () {
test('test LogicValue', () {
final control = Logic(width: 8);
final d0 = Logic(width: 8)..put(LogicValue.ofInt(2, 8));
final d1 = Logic(width: 8)..put(LogicValue.ofInt(3, 8));
final result = cases(
control,
{
d0: LogicValue.ofInt(2, 8),
d1: LogicValue.ofInt(3, 8),
},
width: 8);

control.put(2);

expect(result.value, LogicValue.ofInt(2, 8));

control.put(3);

expect(result.value, LogicValue.ofInt(3, 8));
});

test('test Int', () {
final control = Logic(width: 4)..put(LogicValue.ofInt(2, 4));
const d0 = 2;
const d1 = 3;

final result = cases(
control,
{
d0: 2,
d1: 3,
},
width: 4,
defaultValue: 3);

expect(result.value, LogicValue.ofInt(2, 4));
});

test('test Logic', () {
final control = Logic();
final d0 = Logic()..put(LogicValue.zero);
final d1 = Logic()..put(LogicValue.one);
final result = cases(control, {
d0: LogicValue.zero,
d1: LogicValue.one,
});

control.put(0);
expect(result.value, LogicValue.zero);

control.put(1);
expect(result.value, LogicValue.one);
});

test('test Default', () {
final control = Logic(width: 4);
const d0 = 1;
const d1 = 2;
final result = cases(
control,
{
d0: 1,
d1: 2,
},
width: 4,
defaultValue: 3);

control.put(LogicValue.zero);
expect(result.value, LogicValue.ofInt(3, 4));
});

test('test Exceptions(Int)', () {
final control = Logic(width: 4);
final d0 = Logic(width: 4);
final d1 = Logic(width: 8);

control.put(LogicValue.ofInt(2, 4));
d0.put(LogicValue.ofInt(2, 4));
d1.put(LogicValue.ofInt(3, 8));

expect(() => cases(control, {d0: 2, d1: 3}, width: 4),
throwsA(isA<SignalWidthMismatchException>()));
});

test('test Condition width mismatch Exception', () {
final control = Logic();
final d0 = Logic();
final d1 = Logic(width: 8);

control.put(LogicValue.zero);
d0.put(LogicValue.zero);
d1.put(LogicValue.ofInt(1, 8));

expect(
() =>
cases(control, {d0: LogicValue.zero, d1: LogicValue.ofInt(1, 8)}),
throwsA(isA<SignalWidthMismatchException>()));
});

test('test Expression width mismatch Exception for Logic', () {
final control = Logic();
final d0 = Logic(width: 8);
final d1 = Logic(width: 8);

control.put(LogicValue.one);

expect(
() => cases(control, {d0: Logic(width: 8), d1: Logic(width: 8)},
width: 8),
throwsA(isA<SignalWidthMismatchException>()));
});

test('test Expression width mismatch Exception for LogicValue', () {
final control = Logic();
final d0 = LogicValue.ofInt(0, 8);
final d1 = LogicValue.ofInt(1, 8);

control.put(LogicValue.one);

expect(
() => cases(
control, {d0: LogicValue.ofInt(0, 8), d1: LogicValue.ofInt(1, 8)},
width: 8),
throwsA(isA<SignalWidthMismatchException>()));
});

test('test Null width Exception', () {
final control = Logic();
const d0 = 2;
const d1 = 4;

control.put(LogicValue.zero);
expect(() => cases(control, {d0: 2, d1: 4}, defaultValue: 3),
throwsA(isA<SignalWidthMismatchException>()));
});
});

group('simcompare', () {
test('NotGate single bit', () async {
final gtm = GateTestModule(Logic(), Logic());
Expand Down

0 comments on commit e00e350

Please sign in to comment.