Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cases #386

Merged
merged 14 commits into from
Aug 29, 2023
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.');
}
78 changes: 76 additions & 2 deletions lib/src/modules/conditional.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@
import 'dart:async';
import 'dart:collection';

import 'package:collection/collection.dart';
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';
import 'package:test/test.dart';

/// Represents a block of logic, similar to `always` blocks in SystemVerilog.
abstract class _Always extends Module with CustomSystemVerilog {
Expand Down Expand Up @@ -890,6 +891,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;
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
}

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

for (final condition in conditions.entries) {
if (condition.key is Logic) {
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
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>()));
});
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
mkorbel1 marked this conversation as resolved.
Show resolved Hide resolved
});

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