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

WaveDumper optimization #242

Merged
merged 3 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions benchmark/benchmark.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
import 'byte_enable_benchmark.dart';
import 'logic_value_of_benchmark.dart';
import 'pipeline_benchmark.dart';
import 'wave_dump_benchmark.dart';

void main() async {
await PipelineBenchmark().report();
LogicValueOfBenchmark().report();
ByteEnableBenchmark().report();
await WaveDumpBenchmark().report();
}
73 changes: 73 additions & 0 deletions benchmark/wave_dump_benchmark.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/// Copyright (C) 2023 Intel Corporation
/// SPDX-License-Identifier: BSD-3-Clause
///
/// wave_dump_benchmark.dart
/// Benchmarking for wave dumping
///
/// 2023 January 5
/// Author: Max Korbel <max.korbel@intel.com>
///

import 'dart:io';

import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:rohd/rohd.dart';

class _ModuleToDump extends Module {
static const _numExtraOutputs = 50;

_ModuleToDump(Logic d, Logic clk) {
d = addInput('d', d);

final q = addOutput('q');

for (var i = 0; i < _numExtraOutputs; i++) {
addOutput('i$i') <= FlipFlop(clk, ~output('i$i')).q;
}

q <= FlipFlop(clk, d).q;
}
}

class WaveDumpBenchmark extends AsyncBenchmarkBase {
late _ModuleToDump _mod;
late Logic _clk;

static const _maxSimTime = 1000;

static const _vcdTemporaryPath = 'tmp_test/wave_dump_benchmark.vcd';

WaveDumpBenchmark() : super('WaveDump');

@override
Future<void> setup() async {
Simulator.setMaxSimTime(_maxSimTime);
}

@override
Future<void> teardown() async {
if (File(_vcdTemporaryPath).existsSync()) {
File(_vcdTemporaryPath).deleteSync();
}
}

@override
Future<void> run() async {
_clk = SimpleClockGenerator(10).clk;
_mod = _ModuleToDump(Logic(), _clk);
await _mod.build();

WaveDumper(_mod, outputPath: _vcdTemporaryPath);

await Simulator.run();

assert(Simulator.time == _maxSimTime, 'sim should run through end');

await Simulator.reset();
Simulator.setMaxSimTime(_maxSimTime);
}
}

Future<void> main() async {
await WaveDumpBenchmark().report();
}
39 changes: 30 additions & 9 deletions lib/src/wave_dumper.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// Copyright (C) 2021 Intel Corporation
/// Copyright (C) 2021-2023 Intel Corporation
/// SPDX-License-Identifier: BSD-3-Clause
///
/// wave_dumper.dart
Expand All @@ -18,6 +18,9 @@ import 'package:rohd/src/utilities/uniquifier.dart';
///
/// Outputs to vcd format at [outputPath]. [module] must be built prior to
/// attaching the [WaveDumper].
///
/// The waves will only dump to the file periodically and then once the
/// simulation has completed.
class WaveDumper {
/// The [Module] being dumped.
final Module module;
Expand All @@ -31,6 +34,9 @@ class WaveDumper {
/// A sink to write contents into [_outputFile].
late final IOSink _outFileSink;

/// A buffer for contents before writing to the file sink.
final StringBuffer _fileBuffer = StringBuffer();

/// A counter for tracking signal names in the VCD file.
int _signalMarkerIdx = 0;

Expand Down Expand Up @@ -82,13 +88,28 @@ class WaveDumper {
});
}

/// Writes [contents] to the output file.
void _writeToFile(String contents) {
_outFileSink.write(contents);
/// Number of characters in the buffer after which it will
/// write contents to the output file.
static const _fileBufferLimit = 100000;

/// Buffers [contents] to be written to the output file.
void _writeToBuffer(String contents) {
_fileBuffer.write(contents);

if (_fileBuffer.length > _fileBufferLimit) {
_writeToFile();
}
}

/// Writes all pending items in the [_fileBuffer] to the file.
void _writeToFile() {
_outFileSink.write(_fileBuffer.toString());
_fileBuffer.clear();
}

/// Terminates the waveform dumping, including closing the file.
Future<void> _terminate() async {
_writeToFile();
await _outFileSink.flush();
await _outFileSink.close();
}
Expand Down Expand Up @@ -135,7 +156,7 @@ class WaveDumper {
\$end
\$timescale $timescale \$end
''';
_writeToFile(header);
_writeToBuffer(header);
}

/// Writes the scope of the VCD, including signal and hierarchy declarations,
Expand All @@ -144,9 +165,9 @@ class WaveDumper {
var scopeString = _computeScopeString(module);
scopeString += '\$enddefinitions \$end\n';
scopeString += '\$dumpvars\n';
_writeToFile(scopeString);
_writeToBuffer(scopeString);
_signalToMarkerMap.keys.forEach(_writeSignalValueUpdate);
_writeToFile('\$end\n');
_writeToBuffer('\$end\n');
}

/// Generates the top of the scope string (signal and hierarchy definitions).
Expand Down Expand Up @@ -184,7 +205,7 @@ class WaveDumper {
/// Writes the current timestamp to the VCD.
void _captureTimestamp(int timestamp) {
final timestampString = '#$timestamp\n';
_writeToFile(timestampString);
_writeToBuffer(timestampString);

_changedLogicsThisTimestamp
..forEach(_writeSignalValueUpdate)
Expand All @@ -202,7 +223,7 @@ class WaveDumper {
: signal.value.toString(includeWidth: false);
final marker = _signalToMarkerMap[signal];
final updateString = '$updateValue$marker\n';
_writeToFile(updateString);
_writeToBuffer(updateString);
}
}

Expand Down
5 changes: 5 additions & 0 deletions test/benchmark_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:test/test.dart';
import '../benchmark/byte_enable_benchmark.dart';
import '../benchmark/logic_value_of_benchmark.dart';
import '../benchmark/pipeline_benchmark.dart';
import '../benchmark/wave_dump_benchmark.dart';

void main() {
test('pipeline benchmark', () async {
Expand All @@ -26,4 +27,8 @@ void main() {
test('byte enable benchmark', () {
ByteEnableBenchmark().measure();
});

test('waveform benchmark', () async {
await WaveDumpBenchmark().measure();
});
}
3 changes: 2 additions & 1 deletion test/config_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ void main() async {

createTemporaryDump(mod, dumpName);

await Simulator.run();

final vcdContents = await File(temporaryDumpPath(dumpName)).readAsString();
expect(vcdContents, contains(version));

await Simulator.run();
deleteTemporaryDump(dumpName);
});
}
23 changes: 23 additions & 0 deletions test/wave_dumper_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,27 @@ void main() {

deleteTemporaryDump(dumpName);
});

test('dump after max sim time works', () async {
final a = SimpleClockGenerator(10).clk;
final mod = SimpleModule(a);
await mod.build();

const dumpName = 'maxSimTime';

createTemporaryDump(mod, dumpName);

Simulator.setMaxSimTime(100);

await Simulator.run();

final vcdContents = File(temporaryDumpPath(dumpName)).readAsStringSync();

expect(
VcdParser.confirmValue(vcdContents, 'a', 99, LogicValue.one),
equals(true),
);

deleteTemporaryDump(dumpName);
});
}