diff --git a/doc/README.md b/doc/README.md index 91858e2e..73d05ead 100644 --- a/doc/README.md +++ b/doc/README.md @@ -27,6 +27,8 @@ Some in-development items will have opened issues, as well. Feel free to create - Count - [Count bit occurence](./components/count.md) - Count pattern occurence +- Detection + - Edge detection - Sort - [Bitonic sort](./components/sort.md#bitonic-sort) - Arithmetic @@ -45,6 +47,8 @@ Some in-development items will have opened issues, as well. Feel free to create - BFloat16 (16-bit) - BFloat8 (8-bit) - BFloat4 (4-bit) + - Fixed point + - Binary-Coded Decimal (BCD) - [Rotate](./components/rotate.md) - Counters - Binary counter @@ -85,8 +89,10 @@ Some in-development items will have opened issues, as well. Feel free to create - Models - [APB](./components/apb_bfm.md) - [Ready/Valid](./components/ready_valid_bfm.md) + - SPI + - CXL ---------------- -Copyright (C) 2023 Intel Corporation +Copyright (C) 2023-2024 Intel Corporation SPDX-License-Identifier: BSD-3-Clause diff --git a/lib/src/memory/memory.dart b/lib/src/memory/memory.dart index 6d94b49f..80a6b35f 100644 --- a/lib/src/memory/memory.dart +++ b/lib/src/memory/memory.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // memory.dart @@ -96,6 +96,8 @@ abstract class Memory extends Module { final int dataWidth; /// The number of cycles before data is returned after a read. + /// + /// Must be non-negative. int get readLatency; /// Internal write ports. @@ -115,10 +117,12 @@ abstract class Memory extends Module { Logic get reset => input('reset'); /// Construct a new memory. + /// + /// Must provide at least one port (read or write). Memory(Logic clk, Logic reset, List writePorts, List readPorts, {super.name = 'memory'}) - : assert(writePorts.isNotEmpty && readPorts.isNotEmpty, + : assert(!(writePorts.isEmpty && readPorts.isEmpty), 'Must specify at least one read port or one write port.'), numWrites = writePorts.length, numReads = readPorts.length, @@ -129,6 +133,10 @@ abstract class Memory extends Module { ? writePorts[0].addrWidth : readPorts[0].addrWidth // at least one of these must exist { + if (readLatency < 0) { + throw RohdHclException('Read latency must be non-negative.'); + } + // make sure widths of everything match expectations for (final port in [...writePorts, ...readPorts]) { if (port.addrWidth != addrWidth) { diff --git a/lib/src/models/apb_bfm/apb_tracker.dart b/lib/src/models/apb_bfm/apb_tracker.dart index 69146344..1e77179f 100644 --- a/lib/src/models/apb_bfm/apb_tracker.dart +++ b/lib/src/models/apb_bfm/apb_tracker.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // apb_tracker.dart @@ -44,13 +44,15 @@ class ApbTracker extends Tracker { super.outputFolder, int timeColumnWidth = 12, int selectColumnWidth = 4, + int addrColumnWidth = 12, + int dataColumnWidth = 12, }) : super(name, [ TrackerField(timeField, columnWidth: timeColumnWidth), if (selectColumnWidth > 0) TrackerField(selectField, columnWidth: selectColumnWidth), const TrackerField(typeField, columnWidth: 1), - const TrackerField(addrField, columnWidth: 12), - const TrackerField(dataField, columnWidth: 12), + TrackerField(addrField, columnWidth: addrColumnWidth), + TrackerField(dataField, columnWidth: dataColumnWidth), const TrackerField(strobeField, columnWidth: 4), if (intf.includeSlvErr) const TrackerField(slverrField, columnWidth: 1), diff --git a/lib/src/models/memory_model.dart b/lib/src/models/memory_model.dart index e3b9e9a4..80a204f1 100644 --- a/lib/src/models/memory_model.dart +++ b/lib/src/models/memory_model.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2021-2023 Intel Corporation +// Copyright (C) 2021-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // memory_model.dart @@ -7,8 +7,11 @@ // 2023 June 12 // Author: Max Korbel +import 'dart:async'; + import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; /// A model of a [Memory] which uses a software-based [SparseMemoryStorage] to /// store data. @@ -19,9 +22,6 @@ class MemoryModel extends Memory { /// The memory storage underlying this model. late final MemoryStorage storage; - /// A pre-signal before the output flops of this memory. - late final List _rdDataPre; - /// If true, a positive edge on reset will reset the memory asynchronously. final bool asyncReset; @@ -47,9 +47,6 @@ class MemoryModel extends Memory { } void _buildLogic() { - _rdDataPre = List.generate( - numReads, (index) => Logic(name: 'rdDataPre$index', width: dataWidth)); - if (asyncReset) { reset.posedge.listen((event) { storage.reset(); @@ -63,8 +60,7 @@ class MemoryModel extends Memory { return; } for (final wrPort in wrPorts) { - if (!(wrPort.en.previousValue?.isValid ?? wrPort.en.value.isValid) && - !storage.isEmpty) { + if (!wrPort.en.previousValue!.isValid && !storage.isEmpty) { // storage doesnt have access to `en`, so check ourselves storage.invalidWrite(); return; @@ -91,34 +87,45 @@ class MemoryModel extends Memory { } } } - }); - // on any glitch to read controls, change pre-flop version of read data - for (var i = 0; i < rdPorts.length; i++) { - clk.negedge.listen((event) => _updatePreRead(i)); - - // flop out the read data - var delayedData = _rdDataPre[i]; - for (var delay = 0; delay < readLatency; delay++) { - delayedData = FlipFlop(clk, delayedData).q; + // if we have at least 1 cycle, then we wait to update the data + if (readLatency > 0) { + for (final rdPort in rdPorts) { + if (!rdPort.en.previousValue!.isValid || + !rdPort.en.previousValue!.toBool() || + !rdPort.addr.previousValue!.isValid) { + unawaited(_updateRead( + rdPort, LogicValue.filled(rdPort.dataWidth, LogicValue.x))); + } else { + unawaited(_updateRead(rdPort, storage.readData(rdPort.addr.value))); + } + } } + }); - rdPorts[i].data <= delayedData; + // if latency is 0, we need to update immediately + if (readLatency == 0) { + for (final rdPort in rdPorts) { + rdPort.en.glitch.listen((args) => _updateReadZeroLatency(rdPort)); + rdPort.addr.glitch.listen((args) => _updateReadZeroLatency(rdPort)); + } } } - void _updatePreRead(int rdIndex) { - final rdPort = rdPorts[rdIndex]; - final rdPortPre = _rdDataPre[rdIndex]; - + /// Updates read data for [rdPort] immediately (as if combinationally). + void _updateReadZeroLatency(DataPortInterface rdPort) { if (!rdPort.en.value.isValid || - (rdPort.en.value == LogicValue.one && !rdPort.addr.value.isValid)) { - rdPortPre.put(LogicValue.x, fill: true); - return; + !rdPort.en.value.toBool() || + !rdPort.addr.value.isValid) { + rdPort.data.put(LogicValue.x, fill: true); + } else { + rdPort.data.put(storage.readData(rdPort.addr.value)); } + } - if (rdPort.en.value == LogicValue.one) { - rdPortPre.put(storage.readData(rdPort.addr.value)); - } + /// Updates read data for [rdPort] after [readLatency] time. + Future _updateRead(DataPortInterface rdPort, LogicValue data) async { + await clk.waitCycles(readLatency - 1); + rdPort.data.inject(data); } } diff --git a/pubspec.yaml b/pubspec.yaml index 84d8eece..3a3da72c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ environment: dependencies: meta: ^1.9.1 - rohd: ^0.5.0 + rohd: ^0.5.2 rohd_vf: ^0.5.0 dev_dependencies: diff --git a/test/arbiter_test.dart b/test/arbiter_test.dart index 0c22e7c1..02d1a6fc 100644 --- a/test/arbiter_test.dart +++ b/test/arbiter_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // arbiter_test.dart @@ -91,8 +91,7 @@ void main() { await clk.nextNegedge; expect(grants.value, LogicValue.ofString('00000010')); - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('dynamic request', () async { @@ -142,8 +141,7 @@ void main() { await clk.nextNegedge; expect(grants.value, LogicValue.ofString('10000000')); - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('all reqs', () async { @@ -167,8 +165,7 @@ void main() { await clk.nextNegedge; } - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('all reqs, non-power-of-2', () async { @@ -193,8 +190,7 @@ void main() { await clk.nextNegedge; } - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('beat pattern 2 request maintain fairness', () async { @@ -233,8 +229,7 @@ void main() { await clk.nextPosedge; } - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); // expect an equal number of grants between the two expect(grantCounts[0], grantCounts[1]); diff --git a/test/fifo_test.dart b/test/fifo_test.dart index c25dd4d4..0e09548c 100644 --- a/test/fifo_test.dart +++ b/test/fifo_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // fifo_test.dart @@ -123,8 +123,7 @@ void main() { await clk.nextNegedge; - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('fifo with depth 1', () async { @@ -179,8 +178,7 @@ void main() { expect(fifo.empty.value.toBool(), true); expect(fifo.error!.value.toBool(), false); - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('fifo underflow error without bypass', () async { @@ -218,8 +216,7 @@ void main() { expect(fifo.error!.value.toBool(), true); - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('fifo underflow error with bypass', () async { @@ -258,8 +255,7 @@ void main() { expect(fifo.error!.value.toBool(), true); - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('fifo overflow error', () async { @@ -306,8 +302,7 @@ void main() { expect(fifo.error!.value.toBool(), true); await clk.nextPosedge; - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('fifo empty bypass', () async { @@ -361,8 +356,7 @@ void main() { await clk.nextNegedge; - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('fifo full write and read simultaneously', () async { @@ -422,8 +416,7 @@ void main() { await clk.nextNegedge; expect(fifo.error!.value.toBool(), false); - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); group('fifo peek', () { @@ -474,8 +467,7 @@ void main() { // peek at stable expect(rdData.value.toInt(), 0xfeedbeef); - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); } test('no bypass', () async { diff --git a/test/memory_test.dart b/test/memory_test.dart index 84cbc4ef..0e12022b 100644 --- a/test/memory_test.dart +++ b/test/memory_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // rf_test.dart @@ -13,6 +13,7 @@ import 'dart:async'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; import 'package:test/test.dart'; void main() { @@ -29,29 +30,27 @@ void main() { 'rf': (Logic clk, Logic reset, List wrPorts, List rdPorts) => RegisterFile(clk, reset, wrPorts, rdPorts, numEntries: numEntries), - 'memory model': (Logic clk, Logic reset, List wrPorts, - List rdPorts) => - MemoryModel( - clk, - reset, - wrPorts, - rdPorts, - storage: SparseMemoryStorage( - addrWidth: addrWidth, - dataWidth: dataWidth, - alignAddress: (addr) => addr, - onInvalidRead: (addr, dataWidth) => - LogicValue.filled(dataWidth, LogicValue.zero), - ), - ) + for (var latency = 0; latency <= 2; latency++) + 'memory model (latency $latency)': (Logic clk, + Logic reset, + List wrPorts, + List rdPorts) => + MemoryModel( + clk, + reset, + wrPorts, + rdPorts, + readLatency: latency, + storage: SparseMemoryStorage( + addrWidth: addrWidth, + dataWidth: dataWidth, + alignAddress: (addr) => addr, + onInvalidRead: (addr, dataWidth) => + LogicValue.filled(dataWidth, LogicValue.zero), + ), + ) }; - Future waitCycles(Logic clk, int numCycles) async { - for (var i = 0; i < numCycles; i++) { - await clk.nextNegedge; - } - } - for (final memGen in memoriesToTestGenerators.entries) { final memGenName = memGen.key; final memGenFunc = memGen.value; @@ -99,7 +98,7 @@ void main() { // read it back out on a different port rdPorts[2].en.put(1); rdPorts[2].addr.put(3); - await waitCycles(clk, mem.readLatency); + await clk.waitCycles(mem.readLatency); await clk.nextPosedge; expect(rdPorts[2].data.value.toInt(), 0xdeadbeef); @@ -107,8 +106,7 @@ void main() { rdPorts[2].en.put(0); await clk.nextNegedge; - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('$memGenName wr masked', () async { @@ -155,7 +153,7 @@ void main() { // read it back out on a different port rdPorts[0].en.put(1); rdPorts[0].addr.put(4); - await waitCycles(clk, mem.readLatency); + await clk.waitCycles(mem.readLatency); await clk.nextPosedge; expect(rdPorts[0].data.value.toInt(), 0xff00ff00); @@ -163,8 +161,7 @@ void main() { rdPorts[0].en.put(0); await clk.nextNegedge; - Simulator.endSimulation(); - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); } }); @@ -195,5 +192,16 @@ void main() { ), throwsA(const TypeMatcher())); }); + + test('required minimum ports', () { + RegisterFile(Logic(), Logic(), [], [DataPortInterface(32, 32)]); + RegisterFile(Logic(), Logic(), [DataPortInterface(32, 32)], []); + + try { + RegisterFile(Logic(), Logic(), [], []); + fail('Should have failed'); + // ignore: avoid_catching_errors + } on AssertionError catch (_) {} + }); }); } diff --git a/test/shift_register_test.dart b/test/shift_register_test.dart index 2479006b..eef3ddc4 100644 --- a/test/shift_register_test.dart +++ b/test/shift_register_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Intel Corporation +// Copyright (C) 2023-2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // shift_register_test.dart @@ -48,9 +48,7 @@ void main() { expect(dataOut.value.toInt(), data.last); - Simulator.endSimulation(); - - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); test('shift register naming', () { @@ -113,9 +111,7 @@ void main() { expect(dataOut.value.toInt(), 0); - Simulator.endSimulation(); - - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); group('reset shift register', () { @@ -148,9 +144,7 @@ void main() { expect(dataOut.value.toInt(), 0x45); - Simulator.endSimulation(); - - await Simulator.simulationEnded; + await Simulator.endSimulation(); } test('null reset value', () async { @@ -213,8 +207,6 @@ void main() { expect(dataOut.value.toInt(), 0); - Simulator.endSimulation(); - - await Simulator.simulationEnded; + await Simulator.endSimulation(); }); }