Skip to content

Commit

Permalink
Support for LogicArray ports (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorbel1 authored Nov 29, 2023
1 parent a9af4f7 commit cb7832f
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 21 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ The [ROHD Forum](https://intel.github.io/rohd-website/forum/rohd-forum/) is a pe
You must have [Dart](https://dart.dev/) installed on your system to use ROHD and ROHD Cosim. You can find detailed instructions for how to install Dart here:
<https://dart.dev/get-dart>

To run the complete ROHD Cosim test suite for development, you need to install [Icarus Verilog](http://iverilog.icarus.com/). It is used as the SystemVerilog simulator side of the cosimulation. Installation instructions are available here: <https://iverilog.fandom.com/wiki/Installation_Guide>
To run the complete ROHD Cosim test suite for development, you need to install [Icarus Verilog](https://steveicarus.github.io/iverilog/). It is used as the SystemVerilog simulator side of the cosimulation. Installation instructions are available here: <https://iverilog.fandom.com/wiki/Installation_Guide>

ROHD Cosim relies on a python package called [cocotb](https://docs.cocotb.org/en/stable/) and its GPI library for communicating to SystemVerilog simulators. The cocotb libraries have good support for a variety of simulators and have been used by many silicon and FPGA projects. You will need to have a sufficiently recent version of [Python](https://www.python.org/) installed. Detailed instructions for installing cocotb are available here: <https://docs.cocotb.org/en/stable/install.html>. The instructions generally boil down to:

Expand Down
1 change: 1 addition & 0 deletions lib/src/configs/cosim_wrap_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ class CosimWrapConfig extends CosimProcessConfig {
registreeEntry.value.instantiationVerilog(
'dont_care',
registreeEntry.key,
// ignore: invalid_use_of_protected_member
registreeEntry.value.inputs.map((key, value) => MapEntry(key, '')),
registreeEntry.value.outputs.map((key, value) => MapEntry(key, '')),
)),
Expand Down
99 changes: 82 additions & 17 deletions lib/src/cosim.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
// Author: Max Korbel <[email protected]>

import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';

import 'package:collection/collection.dart';
import 'package:logging/logging.dart';
import 'package:rohd/rohd.dart';
// ignore: implementation_imports
Expand Down Expand Up @@ -258,11 +258,27 @@ mixin Cosim on ExternalSystemVerilogModule {
Future<void> _sendInput(Logic inputSignal, LogicValue newValue) async {
assert(
inputSignal.parentModule! == this, 'Signal should be in this Cosim.');
await _socketLock.synchronized(() async {
_send('DRIVE:'
'${_cosimSignalName(inputSignal)}:'
'${newValue.toString(includeWidth: false)}');
});

if (inputSignal is LogicArray && inputSignal.numUnpackedDimensions > 0) {
var idx = 0;
for (final element in inputSignal.flattenedUnpacked) {
await _socketLock.synchronized(() async {
_send('DRIVE:'
'${_cosimSignalName(inputSignal)}[$idx]:'
'${newValue.getRange(
idx * element.width,
idx * element.width + element.width,
).toString(includeWidth: false)}');
});
idx++;
}
} else {
await _socketLock.synchronized(() async {
_send('DRIVE:'
'${_cosimSignalName(inputSignal)}:'
'${newValue.toString(includeWidth: false)}');
});
}
}

/// Transmits all input values to cosim.
Expand Down Expand Up @@ -300,13 +316,27 @@ mixin Cosim on ExternalSystemVerilogModule {
final signalNameSplit = event.signalName!.split('.');
final registreeName = signalNameSplit[0];
final portName = signalNameSplit[1];

if (!_registrees.containsKey(registreeName)) {
throw Exception('Did not find registered module named "$registreeName",'
' but received a message from the cosim process attempting to'
' drive this signal: "${event.signalName}"');
}
// ignore: unnecessary_null_checks
_registrees[registreeName]!.output(portName).put(event.signalValue!);

// handle case where there was an unpacked array (should end with ']')
if (portName.endsWith(']')) {
final splitPortName = portName.split(RegExp(r'[\[\]]'));
final arrayName = splitPortName[0];
final arrayIndex = int.parse(splitPortName[1]);
(_registrees[registreeName]!.output(arrayName) as LogicArray)
.flattenedUnpacked
.toList()[arrayIndex]
// ignore: unnecessary_null_checks
.put(event.signalValue!);
} else {
// ignore: unnecessary_null_checks
_registrees[registreeName]!.output(portName).put(event.signalValue!);
}
});

_receivedStream
Expand Down Expand Up @@ -545,12 +575,22 @@ async def setup_connections(dut, connector : rohd_connector.RohdConnector):
// no need to listen to 0-bit signals, they probably don't even exist
continue;
}
// ignore: missing_whitespace_between_adjacent_strings
pythonFileContents.write(' cocotb.start_soon('
'connector.listen_to_signal('
"'${registree._cosimSignalName(outputLogic)}',"
' $cocoTbHier.$outputName'
'))\n');
if (outputLogic is LogicArray &&
outputLogic.numUnpackedDimensions > 0) {
for (var i = 0; i < outputLogic.flattenedUnpackedCount; i++) {
pythonFileContents.write(' cocotb.start_soon( '
'connector.listen_to_signal('
"'${registree._cosimSignalName(outputLogic)}[$i]',"
' $cocoTbHier.$outputName[$i] '
'))\n');
}
} else {
pythonFileContents.write(' cocotb.start_soon( '
'connector.listen_to_signal('
"'${registree._cosimSignalName(outputLogic)}',"
' $cocoTbHier.$outputName '
'))\n');
}
}
for (final inputEntry in registree.inputs.entries) {
final inputName = inputEntry.key;
Expand All @@ -559,9 +599,18 @@ async def setup_connections(dut, connector : rohd_connector.RohdConnector):
// no need to drive 0-bit signals, they probably don't even exist
continue;
}
pythonFileContents.write(
" nameToSignalMap['${registree._cosimSignalName(inputLogic)}'] "
'= $cocoTbHier.$inputName\n');

if (inputLogic is LogicArray && inputLogic.numUnpackedDimensions > 0) {
for (var i = 0; i < inputLogic.flattenedUnpackedCount; i++) {
pythonFileContents.write(' nameToSignalMap[ '
"'${registree._cosimSignalName(inputLogic)}[$i]' ] "
'= $cocoTbHier.$inputName[$i]\n');
}
} else {
pythonFileContents.write(' nameToSignalMap[ '
"'${registree._cosimSignalName(inputLogic)}' ] "
'= $cocoTbHier.$inputName\n');
}
}
}
pythonFileContents
Expand All @@ -572,3 +621,19 @@ async def setup_connections(dut, connector : rohd_connector.RohdConnector):
File('$directory/__init__.py').writeAsStringSync('');
}
}

extension on LogicArray {
/// Returns all unpacked dimensions as a flattened iterable.
Iterable<Logic> get flattenedUnpacked {
Iterable<Logic> flattenedElements = elements;
for (var i = 0; i < numUnpackedDimensions - 1; i++) {
flattenedElements = flattenedElements.map((e) => e.elements).flattened;
}
return flattenedElements;
}

/// Returns the number of elements in [flattenedUnpacked].
int get flattenedUnpackedCount => numUnpackedDimensions == 0
? 0
: dimensions.getRange(0, numUnpackedDimensions).fold(1, (a, b) => a * b);
}
5 changes: 3 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ issue_tracker: https://github.com/intel/rohd-cosim/issues
documentation: https://intel.github.io/rohd-cosim/rohd_cosim/rohd_cosim-library.html

environment:
sdk: '>=2.17.0 <3.0.0'
sdk: '>=2.17.0 <4.0.0'

dependencies:
collection: ^1.18.0
logging: ^1.0.2
meta: ^1.8.0
rohd: ^0.4.2
rohd: ^0.5.0
synchronized: ^3.0.0

dev_dependencies:
Expand Down
122 changes: 122 additions & 0 deletions test/array_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// array_test.dart
// Tests for array functionality
//
// 2023 November 28
// Author: Max Korbel <[email protected]>

import 'package:rohd/rohd.dart';
import 'package:rohd/src/utilities/simcompare.dart';
import 'package:rohd_cosim/rohd_cosim.dart';
import 'package:test/test.dart';

import 'cosim_test_infra.dart';

class CosimArrayMod extends ExternalSystemVerilogModule with Cosim {
LogicArray get b => output('b') as LogicArray;

@override
List<String> get verilogSources => ['../../test/cosim_array.sv'];

CosimArrayMod(
Logic a, {
super.name = 'arraymod',
int numUnpackedDimensions = 0,
}) : assert(numUnpackedDimensions >= 0 && numUnpackedDimensions <= 2,
'only supports 0,1,2'),
super(definitionName: 'my_cosim_array_2p${numUnpackedDimensions}u') {
final dimensions = [
if (numUnpackedDimensions >= 2) 5,
if (numUnpackedDimensions >= 1) 4,
3,
];

addInputArray('a', a,
dimensions: dimensions,
elementWidth: 2,
numUnpackedDimensions: numUnpackedDimensions);
addOutputArray('b',
dimensions: dimensions,
elementWidth: 2,
numUnpackedDimensions: numUnpackedDimensions);
}
}

Future<void> main() async {
tearDown(() async {
await Simulator.reset();
await Cosim.reset();
});

group('cosim array', () {
List<Vector> walkingOnes(int width) {
final vectors = <Vector>[];
for (var i = 0; i < width; i++) {
final shiftedOne = LogicValue.ofInt(1, width) << i;
vectors.add(Vector({'a': shiftedOne}, {'b': shiftedOne}));
}
return vectors;
}

test('2 packed, 0 unpacked', () async {
final mod = CosimArrayMod(Logic(width: 6));
await mod.build();

const dirName = 'simple_array_2p0u';

await CosimTestingInfrastructure.connectCosim(dirName);

final vectors = [
Vector({'a': 0}, {'b': 0}),
Vector({'a': LogicValue.ofString('01xz10')},
{'b': LogicValue.ofString('01xz10')}),
...walkingOnes(6)
];
await SimCompare.checkFunctionalVector(mod, vectors);

await CosimTestingInfrastructure.cleanupCosim(dirName);
});

test('2 packed, 1 unpacked', () async {
final mod = CosimArrayMod(Logic(width: 6 * 4), numUnpackedDimensions: 1);
await mod.build();

const dirName = 'simple_array_2p1u';

await CosimTestingInfrastructure.connectCosim(dirName);

final vectors = [
Vector({'a': 0}, {'b': 0}),
Vector({'a': LogicValue.ofString('01xz10' * 4)},
{'b': LogicValue.ofString('01xz10' * 4)}),
...walkingOnes(24),
];

await SimCompare.checkFunctionalVector(mod, vectors);

await CosimTestingInfrastructure.cleanupCosim(dirName);
});

test('2 packed, 2 unpacked', () async {
final mod =
CosimArrayMod(Logic(width: 6 * 4 * 5), numUnpackedDimensions: 2);
await mod.build();

const dirName = 'simple_array_2p2u';

await CosimTestingInfrastructure.connectCosim(dirName);

final vectors = [
Vector({'a': 0}, {'b': 0}),
Vector({'a': LogicValue.ofString('01xz10' * 4 * 5)},
{'b': LogicValue.ofString('01xz10' * 4 * 5)}),
...walkingOnes(120),
];
await SimCompare.checkFunctionalVector(mod, vectors);

await CosimTestingInfrastructure.cleanupCosim(dirName);
});
});
}
38 changes: 38 additions & 0 deletions test/cosim_array.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (C) 2023 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause
//
// cosim_array.sv
// A simple SystemVerilog module for testing cosimulation with arrays.
//
// 2022
// Author: Max Korbel <[email protected]>

// 2 packed, 2 unpacked
module my_cosim_array_2p2u(
input logic[2:0][1:0] a [4:0][3:0],
output logic[2:0][1:0] b [4:0][3:0]
);

assign b = a;

endmodule

// 2 packed, 1 unpacked
module my_cosim_array_2p1u(
input logic[2:0][1:0] a [3:0],
output logic[2:0][1:0] b [3:0]
);

assign b = a;

endmodule

// 2 packed, 0 unpacked
module my_cosim_array_2p0u(
input logic[2:0][1:0] a,
output logic[2:0][1:0] b
);

assign b = a;

endmodule
2 changes: 1 addition & 1 deletion test/cosim_bus.sv
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause
//
// cosim_bus.sv
// A simple SystemVerilog module for testing cosimulation.
// A simple SystemVerilog module for testing cosimulation with busses.
//
// 2022
// Author: Max Korbel <[email protected]>
Expand Down

0 comments on commit cb7832f

Please sign in to comment.