From 3305672a1caf11634a3d9eb83289cc540473fdd7 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Tue, 24 Sep 2024 10:03:11 -0700 Subject: [PATCH 01/31] first spi and spi test commit --- lib/src/interfaces/interfaces.dart | 1 + lib/src/interfaces/spi.dart | 50 +++++++++++++++++++++++++ test/spi_test | 60 ++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 lib/src/interfaces/spi.dart create mode 100644 test/spi_test diff --git a/lib/src/interfaces/interfaces.dart b/lib/src/interfaces/interfaces.dart index 45914619e..39acc9bc7 100644 --- a/lib/src/interfaces/interfaces.dart +++ b/lib/src/interfaces/interfaces.dart @@ -2,3 +2,4 @@ // SPDX-License-Identifier: BSD-3-Clause export 'apb.dart'; +export 'spi.dart'; diff --git a/lib/src/interfaces/spi.dart b/lib/src/interfaces/spi.dart new file mode 100644 index 000000000..25e3f2f80 --- /dev/null +++ b/lib/src/interfaces/spi.dart @@ -0,0 +1,50 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi.dart +// Definitions for the SPI interface. +// +// 2024 September 23 +// Author: Roberto Torres + +import 'package:rohd/rohd.dart'; +//import 'package:rohd_hcl/src/exceptions.dart'; + +/// A standard SPI interface. +class SpiInterface extends PairInterface { + // The width of the data ports [mosi] and [miso]. + // final int dataWidth; + + /// The width of the chip select port [cs]. + // CS as individual lines or one + // final int csWidth; + + /// + Logic get clk => port('CLK'); + Logic get mosi => port('MOSI'); + Logic get miso => port('MISO'); + Logic get cs => port('CSB'); //CS bar + + SpiInterface() + : super( + portsFromConsumer: [Port('MISO')], + portsFromProvider: [Port('MOSI'), Port('CSB'), Port('CLK')], + modify: (original) => 'spi_$original', + ); + + SpiInterface.clone(SpiInterface super.otherInterface) : super.clone(); +} + +class SpiMain extends Module { + late final SpiInterface _intf; + SpiMain(SpiInterface intf) { + _intf = + SpiInterface.clone(intf).pairConnectIO(this, intf, PairRole.provider); + } +} + +class SpiSub extends Module { + SpiSub(SpiInterface intf) { + SpiInterface.clone(intf).pairConnectIO(this, intf, PairRole.consumer); + } +} diff --git a/test/spi_test b/test/spi_test new file mode 100644 index 000000000..e36e2314c --- /dev/null +++ b/test/spi_test @@ -0,0 +1,60 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_test.dart +// Tests for SPI interface +// +// 2024 September 23 +// Author: Roberto Torres + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:test/test.dart'; + +class SpiMain extends Module { + final SpiInterface spiInterface; + + SpiMain() : spiInterface = SpiInterface() { + // Add the SPI interface to the module + addOutput(spiInterface.clk); + addOutput(spiInterface.mosi); + addInput(spiInterface.miso); + addOutput(spiInterface.cs); + } +} + +class SpiSub extends Module { + final SpiInterface spiInterface; + + SpiSub() : spiInterface = SpiInterface() { + // Add the SPI interface to the module + addInput(spiInterface.clk); + addInput(spiInterface.mosi); + addOutput(spiInterface.miso); + addInput(spiInterface.cs); + } +} + +void main() { + // Instantiate the master and slave modules + final main = SpiMain(); + final sub = SpiSub(); + + // Connect the SPI interfaces + main.spiInterface.pairConnectIO( + main, + sub.spiInterface, + PairRole.provider, + ); + + // Optionally, you can also connect the slave to the master + sub.spiInterface.pairConnectIO( + slave, + master.spiInterface, + PairRole.consumer, + ); + + // Build the design + final design = Design([master, slave]); + design.build(); +} \ No newline at end of file From d6a038681b423a3c2e23be52aec0373d1e118163 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Tue, 24 Sep 2024 14:47:16 -0700 Subject: [PATCH 02/31] spi_test initial draft --- lib/src/interfaces/spi.dart | 23 +++++++------- test/spi_test | 60 ------------------------------------- test/spi_test.dart | 48 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 73 deletions(-) delete mode 100644 test/spi_test create mode 100644 test/spi_test.dart diff --git a/lib/src/interfaces/spi.dart b/lib/src/interfaces/spi.dart index 25e3f2f80..fd305d38e 100644 --- a/lib/src/interfaces/spi.dart +++ b/lib/src/interfaces/spi.dart @@ -21,30 +21,27 @@ class SpiInterface extends PairInterface { /// Logic get clk => port('CLK'); + + /// Logic get mosi => port('MOSI'); + + /// Logic get miso => port('MISO'); + + /// Logic get cs => port('CSB'); //CS bar + /// SpiInterface() : super( portsFromConsumer: [Port('MISO')], portsFromProvider: [Port('MOSI'), Port('CSB'), Port('CLK')], - modify: (original) => 'spi_$original', ); + /// SpiInterface.clone(SpiInterface super.otherInterface) : super.clone(); -} -class SpiMain extends Module { - late final SpiInterface _intf; - SpiMain(SpiInterface intf) { - _intf = - SpiInterface.clone(intf).pairConnectIO(this, intf, PairRole.provider); - } + // multiple CS or 4 bits in parallel } -class SpiSub extends Module { - SpiSub(SpiInterface intf) { - SpiInterface.clone(intf).pairConnectIO(this, intf, PairRole.consumer); - } -} +// place for spi mode = cpol and cpha diff --git a/test/spi_test b/test/spi_test deleted file mode 100644 index e36e2314c..000000000 --- a/test/spi_test +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: BSD-3-Clause -// -// spi_test.dart -// Tests for SPI interface -// -// 2024 September 23 -// Author: Roberto Torres - -import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:test/test.dart'; - -class SpiMain extends Module { - final SpiInterface spiInterface; - - SpiMain() : spiInterface = SpiInterface() { - // Add the SPI interface to the module - addOutput(spiInterface.clk); - addOutput(spiInterface.mosi); - addInput(spiInterface.miso); - addOutput(spiInterface.cs); - } -} - -class SpiSub extends Module { - final SpiInterface spiInterface; - - SpiSub() : spiInterface = SpiInterface() { - // Add the SPI interface to the module - addInput(spiInterface.clk); - addInput(spiInterface.mosi); - addOutput(spiInterface.miso); - addInput(spiInterface.cs); - } -} - -void main() { - // Instantiate the master and slave modules - final main = SpiMain(); - final sub = SpiSub(); - - // Connect the SPI interfaces - main.spiInterface.pairConnectIO( - main, - sub.spiInterface, - PairRole.provider, - ); - - // Optionally, you can also connect the slave to the master - sub.spiInterface.pairConnectIO( - slave, - master.spiInterface, - PairRole.consumer, - ); - - // Build the design - final design = Design([master, slave]); - design.build(); -} \ No newline at end of file diff --git a/test/spi_test.dart b/test/spi_test.dart new file mode 100644 index 000000000..e4cb6cf8f --- /dev/null +++ b/test/spi_test.dart @@ -0,0 +1,48 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_test.dart +// Tests for SPI interface +// +// 2024 September 23 +// Author: Roberto Torres + +import 'package:rohd/rohd.dart'; +//import 'package:rohd/src/utilities/simcompare.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:test/test.dart'; + +class SpiMain extends Module { + SpiMain(SpiInterface intf) { + intf = SpiInterface.clone(intf) + ..pairConnectIO(this, intf, PairRole.provider); + } +} + +class SpiSub extends Module { + SpiSub(SpiInterface intf) { + intf = SpiInterface.clone(intf) + ..pairConnectIO(this, intf, PairRole.consumer); + } +} + +class SpiTop extends Module { + SpiTop() { + final intf = SpiInterface(); + SpiMain(intf); + SpiSub(intf); + addOutput('dummy') <= intf.clk; + } +} + +void main() { + tearDown(() async { + await Simulator.reset(); + }); + + test('spi_test', () async { + final mod = SpiTop(); + await mod.build(); + print(mod.generateSynth()); + }); +} From ca33ba4064eac65ec522b77fa63159eb26325afd Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Thu, 26 Sep 2024 18:56:14 -0700 Subject: [PATCH 03/31] spi bfm initial draft --- lib/src/interfaces/spi.dart | 4 +- lib/src/models/models.dart | 1 + lib/src/models/spi_bfm/spi_bfm.dart | 16 +++ lib/src/models/spi_bfm/spi_main.dart | 48 +++++++++ lib/src/models/spi_bfm/spi_main_driver.dart | 85 ++++++++++++++++ lib/src/models/spi_bfm/spi_monitor.dart | 24 +++++ lib/src/models/spi_bfm/spi_packet.dart | 33 +++++++ lib/src/models/spi_bfm/spi_sub.dart | 46 +++++++++ lib/src/models/spi_bfm/spi_sub_driver.dart | 68 +++++++++++++ lib/src/models/spi_bfm/spi_tracker.dart | 40 ++++++++ test/spi_bfm_test.dart | 104 ++++++++++++++++++++ test/spi_test.dart | 2 +- 12 files changed, 468 insertions(+), 3 deletions(-) create mode 100644 lib/src/models/spi_bfm/spi_bfm.dart create mode 100644 lib/src/models/spi_bfm/spi_main.dart create mode 100644 lib/src/models/spi_bfm/spi_main_driver.dart create mode 100644 lib/src/models/spi_bfm/spi_monitor.dart create mode 100644 lib/src/models/spi_bfm/spi_packet.dart create mode 100644 lib/src/models/spi_bfm/spi_sub.dart create mode 100644 lib/src/models/spi_bfm/spi_sub_driver.dart create mode 100644 lib/src/models/spi_bfm/spi_tracker.dart create mode 100644 test/spi_bfm_test.dart diff --git a/lib/src/interfaces/spi.dart b/lib/src/interfaces/spi.dart index fd305d38e..c6a52af9c 100644 --- a/lib/src/interfaces/spi.dart +++ b/lib/src/interfaces/spi.dart @@ -20,7 +20,7 @@ class SpiInterface extends PairInterface { // final int csWidth; /// - Logic get clk => port('CLK'); + Logic get sclk => port('SCLK'); /// Logic get mosi => port('MOSI'); @@ -35,7 +35,7 @@ class SpiInterface extends PairInterface { SpiInterface() : super( portsFromConsumer: [Port('MISO')], - portsFromProvider: [Port('MOSI'), Port('CSB'), Port('CLK')], + portsFromProvider: [Port('MOSI'), Port('CSB'), Port('SCLK')], ); /// diff --git a/lib/src/models/models.dart b/lib/src/models/models.dart index 617bf5ab5..c94f744a8 100644 --- a/lib/src/models/models.dart +++ b/lib/src/models/models.dart @@ -5,3 +5,4 @@ export 'apb_bfm/apb_bfm.dart'; export 'memory_model.dart'; export 'ready_valid_bfm/ready_valid_bfm.dart'; export 'sparse_memory_storage.dart'; +export 'spi_bfm/spi_bfm.dart'; diff --git a/lib/src/models/spi_bfm/spi_bfm.dart b/lib/src/models/spi_bfm/spi_bfm.dart new file mode 100644 index 000000000..dc0113d6a --- /dev/null +++ b/lib/src/models/spi_bfm/spi_bfm.dart @@ -0,0 +1,16 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_bfm.dart +// BFM for SPI interface +// +// 2024 September 23 +// Author: Roberto Torres + +export 'spi_main.dart'; +export 'spi_main_driver.dart'; +export 'spi_monitor.dart'; +export 'spi_packet.dart'; +export 'spi_sub.dart'; +export 'spi_sub_driver.dart'; +export 'spi_tracker.dart'; diff --git a/lib/src/models/spi_bfm/spi_main.dart b/lib/src/models/spi_bfm/spi_main.dart new file mode 100644 index 000000000..a7112d80d --- /dev/null +++ b/lib/src/models/spi_bfm/spi_main.dart @@ -0,0 +1,48 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_main.dart +// An agent for the main side of the SPI interface. +// +// 2024 September 23 +// Author: Roberto Torres + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; + +/// An agent for the main side of the [SpiInterface]. +/// +/// +class SpiMainAgent extends Agent { + /// The interface to drive. + final SpiInterface intf; + + /// The sequencer + late final Sequencer sequencer; + + /// The driver that sends packets. + late final SpiMainDriver driver; + + /// The number of cycles before dropping an objection. + final int dropDelayCycles; + + /// Creates a new [SpiMainAgent]. + SpiMainAgent({ + required this.intf, + required Component parent, + required Logic clk, + String name = 'spiMain', + this.dropDelayCycles = 30, + }) : super(name, parent) { + sequencer = Sequencer('sequencer', this); + + driver = SpiMainDriver( + parent: this, + intf: intf, + clk: clk, + sequencer: sequencer, + dropDelayCycles: dropDelayCycles, + ); + } +} diff --git a/lib/src/models/spi_bfm/spi_main_driver.dart b/lib/src/models/spi_bfm/spi_main_driver.dart new file mode 100644 index 000000000..0e9b9549d --- /dev/null +++ b/lib/src/models/spi_bfm/spi_main_driver.dart @@ -0,0 +1,85 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_main_driver.dart +// A driver for SPI Main. +// +// 2024 September 23 +// Author: Roberto Torres + +import 'dart:async'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; + +/// A driver for the main side of the [SpiInterface]. +/// +/// Driven packets will update the returned data into the same packet. +class SpiMainDriver extends PendingClockedDriver { + /// The interface to drive. + final SpiInterface intf; + + /// Creates a new [SpiMainDriver]. + SpiMainDriver({ + required Component parent, + required this.intf, + required super.clk, + required super.sequencer, + super.dropDelayCycles = 30, + String name = 'spiMainDriver', + }) : super(name, parent); + + @override + Future run(Phase phase) async { + unawaited(super.run(phase)); + + Simulator.injectAction(() { + intf.cs.put(1); + intf.sclk.put(0); + intf.mosi.put(0); + }); + + while (!Simulator.simulationHasEnded) { + if (pendingSeqItems.isNotEmpty) { + await _drivePacket(pendingSeqItems.removeFirst()); + } else { + await clk.nextPosedge; + Simulator.injectAction(() { + intf.cs.put(1); + intf.sclk.put(0); + intf.mosi.put(0); + }); + } + } + } + + /// Drives a packet onto the interface. + Future _drivePacket(SpiPacket packet) async { + // first, setup + await clk.nextPosedge; + // If not selecting this intf, then select it? CS here? + // if (!intf.cs.value.toBool()) { + // intf.cs.put(0); + // } + + intf.cs.inject(0); + + // will be extended to multiple CS + + // Loop through the bits of the packet + for (var i = 0; i < packet.data.width; i++) { + Simulator.injectAction(() { + intf.mosi.put(packet.data[i]); + intf.sclk.put(1); + }); + // Wait for the next clock cycle + await clk.nextNegedge; + Simulator.injectAction(() { + intf.sclk.put(0); + }); + await clk.nextPosedge; + } + // wait for miso to be ready? + } +} diff --git a/lib/src/models/spi_bfm/spi_monitor.dart b/lib/src/models/spi_bfm/spi_monitor.dart new file mode 100644 index 000000000..799355082 --- /dev/null +++ b/lib/src/models/spi_bfm/spi_monitor.dart @@ -0,0 +1,24 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_monitor.dart +// A monitor that watches the SPI interface. +// +// 2024 September 23 +// Author: Roberto Torres + +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; + +/// A monitor for [SpiInterface]s. +class SpiMonitor extends Monitor { + /// The interface to watch. + final SpiInterface intf; + + /// Creates a new [SpiMonitor] for [intf]. + SpiMonitor( + {required this.intf, + required Component parent, + String name = 'spiMonitor'}) + : super(name, parent); +} diff --git a/lib/src/models/spi_bfm/spi_packet.dart b/lib/src/models/spi_bfm/spi_packet.dart new file mode 100644 index 000000000..e4566ff41 --- /dev/null +++ b/lib/src/models/spi_bfm/spi_packet.dart @@ -0,0 +1,33 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_packet.dart +// Packet the SPI interface. +// +// 2024 September 23 +// Author: Roberto Torres + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; + +/// A packet for the [SpiInterface]. +class SpiPacket extends SequenceItem implements Trackable { + /// The data in the packet. + final LogicValue data; + + /// Creates a new packet. + SpiPacket({required this.data}); + + @override + String? trackerString(TrackerField field) { + switch (field.title) { + case SpiTracker.timeField: + return Simulator.time.toString(); + case SpiTracker.dataField: + return data.toString(); + } + + return trackerString(field); + } +} diff --git a/lib/src/models/spi_bfm/spi_sub.dart b/lib/src/models/spi_bfm/spi_sub.dart new file mode 100644 index 000000000..3d3587b25 --- /dev/null +++ b/lib/src/models/spi_bfm/spi_sub.dart @@ -0,0 +1,46 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_sub.dart +// An agent for the sub side of the SPI interface. +// +// 2024 September 23 +// Author: Roberto Torres + +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; + +/// A model for the sub side of the SPI interface. +class SpiSubAgent extends Agent { + /// The interface to drive. + final SpiInterface intf; + + /// The sequencer + late final Sequencer sequencer; + + /// The driver that sends packets. + late final SpiSubDriver driver; + + /// The monitor that watches the interface. + late final SpiMonitor monitor; + + /// Creates a new [SpiSubAgent]. + SpiSubAgent({ + required this.intf, + required Component parent, + String name = 'spiSub', + }) : super(name, parent) { + sequencer = Sequencer('sequencer', this); + + driver = SpiSubDriver( + parent: this, + intf: intf, + sequencer: sequencer, + ); + + monitor = SpiMonitor( + parent: this, + intf: intf, + ); + } +} diff --git a/lib/src/models/spi_bfm/spi_sub_driver.dart b/lib/src/models/spi_bfm/spi_sub_driver.dart new file mode 100644 index 000000000..10ddcc191 --- /dev/null +++ b/lib/src/models/spi_bfm/spi_sub_driver.dart @@ -0,0 +1,68 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_sub_driver.dart +// A driver for SPI Sub. +// +// 2024 September 23 +// Author: Roberto Torres + +import 'dart:async'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; + +/// A driver for the main side of the [SpiInterface]. +/// +/// Driven packets will update the returned data into the same packet. +class SpiSubDriver extends PendingDriver { + /// The interface to drive. + final SpiInterface intf; + + /// Creates a new [SpiSubDriver]. + SpiSubDriver({ + required Component parent, + required this.intf, + required super.sequencer, + String name = 'spiSubDriver', + }) : super(name, parent); + + @override + Future run(Phase phase) async { + unawaited(super.run(phase)); + + Simulator.injectAction(() { + intf.miso.inject('z'); //high impedance + }); + + while (!Simulator.simulationHasEnded) { + if (pendingSeqItems.isNotEmpty) { + await _drivePacket(pendingSeqItems.removeFirst()); + } else { + Simulator.injectAction(() { + intf.miso.put('z'); + }); + } + } + } + // maybe not necessary + // Simulator.injectAction(() { + // intf.miso.put(0); + //}); + + /// Drives a packet onto the interface. + Future _drivePacket(SpiPacket packet) async { + // Loop through the bits of the packet + for (var i = 0; i < packet.data.width; i++) { + intf.sclk.posedge.listen((_) { + Simulator.injectAction(() { + intf.miso.put(packet.data[i]); + }); + }); + } + // Wait for the next clock cycle + } + + // wait for miso to be ready? +} diff --git a/lib/src/models/spi_bfm/spi_tracker.dart b/lib/src/models/spi_bfm/spi_tracker.dart new file mode 100644 index 000000000..128d17a68 --- /dev/null +++ b/lib/src/models/spi_bfm/spi_tracker.dart @@ -0,0 +1,40 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_tracker.dart +// A monitor that watches the SPI interface. +// +// 2024 September 23 +// Author: Roberto Torres + +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; + +/// A tracker for [SpiInterface]. +class SpiTracker extends Tracker { + /// The interface to watch. + final SpiInterface intf; + + /// Tracker field for simulation time. + static const timeField = 'time'; + + /// Tracker field for data. + static const dataField = 'data'; + + /// Tracker field for CS? + //static const csField = 'csb'; + + /// Creates a new tracker for [SpiInterface]. + SpiTracker({ + required this.intf, + String name = 'spiTracker', + super.dumpJson, + super.dumpTable, + super.outputFolder, + int timeColumnWidth = 8, + int dataColumnWidth = 8, + }) : super(name, [ + TrackerField(timeField, columnWidth: timeColumnWidth), + TrackerField(dataField, columnWidth: dataColumnWidth), + ]); +} diff --git a/test/spi_bfm_test.dart b/test/spi_bfm_test.dart new file mode 100644 index 000000000..23cf708ac --- /dev/null +++ b/test/spi_bfm_test.dart @@ -0,0 +1,104 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_bfm_test.dart +// Definitions for the SPI interface. +// +// 2024 September 23 +// Author: Roberto Torres + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; +import 'package:test/test.dart'; + +import 'spi_test.dart'; + +class SpiBfmTest extends Test { + late final SpiInterface intf; + late final SpiMainAgent main; + late final SpiSubAgent sub; + final int numTransfers; + + String get outFolder => 'tmp_test/spibfm/$name/'; + + SpiBfmTest( + super.name, { + this.numTransfers = 1, + }) : super() { + intf = SpiInterface(); + // ? is this how we want to drive sclk? + final clk = SimpleClockGenerator(10).clk; + + main = SpiMainAgent(intf: intf, parent: this, clk: clk); + + sub = SpiSubAgent(intf: intf, parent: this); + + final monitor = SpiMonitor(intf: intf, parent: this); + + Directory(outFolder).createSync(recursive: true); + + final tracker = + SpiTracker(intf: intf, dumpTable: false, outputFolder: outFolder); + + Simulator.registerEndOfSimulationAction(() async { + await tracker.terminate(); + + final jsonStr = File('$outFolder/spi.json').readAsStringSync(); + final jsonContents = json.decode(jsonStr); + // ignore: avoid_dynamic_calls + expect(jsonContents['records'].length, 2 * numTransfers); + + Directory(outFolder).deleteSync(recursive: true); + }); + + monitor.stream.listen(tracker.record); + } + + //int numTransfersCompleted = 0; + + @override + Future run(Phase phase) async { + unawaited(super.run(phase)); + + final obj = phase.raiseObjection('spiBfmTestObj'); + + // final randomData = List.generate(numTransfers, + // (index) => LogicValue.ofInt(Test.random!.nextInt(1 << 32), 32)); + + // for (var i = 0; i < numTransfers; i++) { + // final packets = SpiPacket(data: randomData[i]); + + main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xB, 4))); + sub.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xA, 4))); + // numTransfersCompleted++; + // } + obj.drop(); + } +} + +void main() { + tearDown(() async { + await Simulator.reset(); + }); + + Future runTest(SpiBfmTest spiBfmTest, {bool dumpWaves = false}) async { + Simulator.setMaxSimTime(6000); + + if (dumpWaves) { + final mod = SpiSub(spiBfmTest.intf); + await mod.build(); + WaveDumper(mod); + } + + await spiBfmTest.start(); + } + + test('simple writes and reads', () async { + await runTest(SpiBfmTest('simple')); + }); +} diff --git a/test/spi_test.dart b/test/spi_test.dart index e4cb6cf8f..c81f488cd 100644 --- a/test/spi_test.dart +++ b/test/spi_test.dart @@ -31,7 +31,7 @@ class SpiTop extends Module { final intf = SpiInterface(); SpiMain(intf); SpiSub(intf); - addOutput('dummy') <= intf.clk; + addOutput('dummy') <= intf.sclk; } } From d5d9afe2cce657812022a53a0b255dddcb02c844 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Fri, 27 Sep 2024 11:07:50 -0700 Subject: [PATCH 04/31] debugging hang up --- lib/src/models/spi_bfm/spi_main_driver.dart | 1 + lib/src/models/spi_bfm/spi_sub_driver.dart | 41 +++++++++++---------- test/spi_bfm_test.dart | 6 ++- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/lib/src/models/spi_bfm/spi_main_driver.dart b/lib/src/models/spi_bfm/spi_main_driver.dart index 0e9b9549d..cf71e7d79 100644 --- a/lib/src/models/spi_bfm/spi_main_driver.dart +++ b/lib/src/models/spi_bfm/spi_main_driver.dart @@ -70,6 +70,7 @@ class SpiMainDriver extends PendingClockedDriver { // Loop through the bits of the packet for (var i = 0; i < packet.data.width; i++) { Simulator.injectAction(() { + logger.info('Driving main packet $i'); intf.mosi.put(packet.data[i]); intf.sclk.put(1); }); diff --git a/lib/src/models/spi_bfm/spi_sub_driver.dart b/lib/src/models/spi_bfm/spi_sub_driver.dart index 10ddcc191..4fc5b57ec 100644 --- a/lib/src/models/spi_bfm/spi_sub_driver.dart +++ b/lib/src/models/spi_bfm/spi_sub_driver.dart @@ -33,18 +33,30 @@ class SpiSubDriver extends PendingDriver { unawaited(super.run(phase)); Simulator.injectAction(() { - intf.miso.inject('z'); //high impedance + intf.miso.inject(0); //high impedance }); - while (!Simulator.simulationHasEnded) { - if (pendingSeqItems.isNotEmpty) { - await _drivePacket(pendingSeqItems.removeFirst()); + SpiPacket? packet; + + int? dataIndex; + + intf.sclk.posedge.listen((_) { + if (packet == null && pendingSeqItems.isNotEmpty) { + packet = pendingSeqItems.removeFirst(); + dataIndex = 0; + } + if (packet != null) { + logger.info('driving data index $dataIndex of sub packet'); + intf.miso.inject(packet!.data[dataIndex!]); + dataIndex = dataIndex! + 1; + if (dataIndex! >= packet!.data.width) { + packet = null; + dataIndex = null; + } } else { - Simulator.injectAction(() { - intf.miso.put('z'); - }); + intf.miso.inject(0); } - } + }); } // maybe not necessary // Simulator.injectAction(() { @@ -52,17 +64,8 @@ class SpiSubDriver extends PendingDriver { //}); /// Drives a packet onto the interface. - Future _drivePacket(SpiPacket packet) async { - // Loop through the bits of the packet - for (var i = 0; i < packet.data.width; i++) { - intf.sclk.posedge.listen((_) { - Simulator.injectAction(() { - intf.miso.put(packet.data[i]); - }); - }); - } - // Wait for the next clock cycle - } + + // Wait for the next clock cycle // wait for miso to be ready? } diff --git a/test/spi_bfm_test.dart b/test/spi_bfm_test.dart index 23cf708ac..3fd3d95e7 100644 --- a/test/spi_bfm_test.dart +++ b/test/spi_bfm_test.dart @@ -67,17 +67,19 @@ class SpiBfmTest extends Test { final obj = phase.raiseObjection('spiBfmTestObj'); + logger.info('spi'); // final randomData = List.generate(numTransfers, // (index) => LogicValue.ofInt(Test.random!.nextInt(1 << 32), 32)); // for (var i = 0; i < numTransfers; i++) { // final packets = SpiPacket(data: randomData[i]); - main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xB, 4))); - sub.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xA, 4))); + //main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xB, 4))); + //sub.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xA, 4))); // numTransfersCompleted++; // } obj.drop(); + logger.info('Done run test'); } } From 4eec5524d0c1fd9b1d294104acb02df4f0f7d73d Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Fri, 27 Sep 2024 14:21:09 -0700 Subject: [PATCH 05/31] temp fix for debug --- test/spi_bfm_test.dart | 23 +++++++++++++---------- test/spi_test.dart | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/test/spi_bfm_test.dart b/test/spi_bfm_test.dart index 3fd3d95e7..4bd2c8e3d 100644 --- a/test/spi_bfm_test.dart +++ b/test/spi_bfm_test.dart @@ -24,14 +24,14 @@ class SpiBfmTest extends Test { late final SpiSubAgent sub; final int numTransfers; - String get outFolder => 'tmp_test/spibfm/$name/'; + String get outFolder => 'tmp_test/spibfm/$name'; SpiBfmTest( super.name, { - this.numTransfers = 1, + this.numTransfers = 2, }) : super() { intf = SpiInterface(); - // ? is this how we want to drive sclk? + final clk = SimpleClockGenerator(10).clk; main = SpiMainAgent(intf: intf, parent: this, clk: clk); @@ -48,10 +48,12 @@ class SpiBfmTest extends Test { Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); - final jsonStr = File('$outFolder/spi.json').readAsStringSync(); + final jsonStr = + File('$outFolder/spiTracker.tracker.json').readAsStringSync(); final jsonContents = json.decode(jsonStr); + // ignore: avoid_dynamic_calls - expect(jsonContents['records'].length, 2 * numTransfers); + expect(jsonContents['records'].length, 0); Directory(outFolder).deleteSync(recursive: true); }); @@ -74,8 +76,8 @@ class SpiBfmTest extends Test { // for (var i = 0; i < numTransfers; i++) { // final packets = SpiPacket(data: randomData[i]); - //main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xB, 4))); - //sub.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xA, 4))); + main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xB, 4))); //0b1011 + sub.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xA, 4))); //0b1010 // numTransfersCompleted++; // } obj.drop(); @@ -88,19 +90,20 @@ void main() { await Simulator.reset(); }); - Future runTest(SpiBfmTest spiBfmTest, {bool dumpWaves = false}) async { + Future runTest(SpiBfmTest spiBfmTest, {bool dumpWaves = true}) async { Simulator.setMaxSimTime(6000); if (dumpWaves) { - final mod = SpiSub(spiBfmTest.intf); + final mod = SpiMain(spiBfmTest.intf); await mod.build(); + //print(mod.generateSynth()); WaveDumper(mod); } await spiBfmTest.start(); } - test('simple writes and reads', () async { + test('simple transfers', () async { await runTest(SpiBfmTest('simple')); }); } diff --git a/test/spi_test.dart b/test/spi_test.dart index c81f488cd..406500c13 100644 --- a/test/spi_test.dart +++ b/test/spi_test.dart @@ -43,6 +43,6 @@ void main() { test('spi_test', () async { final mod = SpiTop(); await mod.build(); - print(mod.generateSynth()); + //print(mod.generateSynth()); }); } From d8918977831557d21f07e809caadc7b0b587b52a Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Fri, 27 Sep 2024 15:26:38 -0700 Subject: [PATCH 06/31] basic functionality tested, accurate waveforms produced --- lib/src/models/spi_bfm/spi_main.dart | 8 ++++++++ lib/src/models/spi_bfm/spi_monitor.dart | 1 + lib/src/models/spi_bfm/spi_packet.dart | 2 ++ test/spi_bfm_test.dart | 9 +++++---- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/src/models/spi_bfm/spi_main.dart b/lib/src/models/spi_bfm/spi_main.dart index a7112d80d..585580169 100644 --- a/lib/src/models/spi_bfm/spi_main.dart +++ b/lib/src/models/spi_bfm/spi_main.dart @@ -24,6 +24,9 @@ class SpiMainAgent extends Agent { /// The driver that sends packets. late final SpiMainDriver driver; + /// The monitor that watches the interface. + late final SpiMonitor monitor; + /// The number of cycles before dropping an objection. final int dropDelayCycles; @@ -44,5 +47,10 @@ class SpiMainAgent extends Agent { sequencer: sequencer, dropDelayCycles: dropDelayCycles, ); + + monitor = SpiMonitor( + parent: this, + intf: intf, + ); } } diff --git a/lib/src/models/spi_bfm/spi_monitor.dart b/lib/src/models/spi_bfm/spi_monitor.dart index 799355082..1d31de2c9 100644 --- a/lib/src/models/spi_bfm/spi_monitor.dart +++ b/lib/src/models/spi_bfm/spi_monitor.dart @@ -22,3 +22,4 @@ class SpiMonitor extends Monitor { String name = 'spiMonitor'}) : super(name, parent); } +// add switch for mosi vs miso diff --git a/lib/src/models/spi_bfm/spi_packet.dart b/lib/src/models/spi_bfm/spi_packet.dart index e4566ff41..60b58bd8c 100644 --- a/lib/src/models/spi_bfm/spi_packet.dart +++ b/lib/src/models/spi_bfm/spi_packet.dart @@ -31,3 +31,5 @@ class SpiPacket extends SequenceItem implements Trackable { return trackerString(field); } } + +// add switch for mosi vs miso \ No newline at end of file diff --git a/test/spi_bfm_test.dart b/test/spi_bfm_test.dart index 4bd2c8e3d..95bef7f68 100644 --- a/test/spi_bfm_test.dart +++ b/test/spi_bfm_test.dart @@ -69,19 +69,17 @@ class SpiBfmTest extends Test { final obj = phase.raiseObjection('spiBfmTestObj'); - logger.info('spi'); // final randomData = List.generate(numTransfers, // (index) => LogicValue.ofInt(Test.random!.nextInt(1 << 32), 32)); // for (var i = 0; i < numTransfers; i++) { // final packets = SpiPacket(data: randomData[i]); - main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xB, 4))); //0b1011 - sub.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xA, 4))); //0b1010 + main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xCB, 8))); //0b11001011 + sub.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x1A, 8))); //0b00011010 // numTransfersCompleted++; // } obj.drop(); - logger.info('Done run test'); } } @@ -107,3 +105,6 @@ void main() { await runTest(SpiBfmTest('simple')); }); } + + // test for real use case. 8 bit sent and 8 bit response + From a226460a92285bb437a380e0afed2f7185cb3823 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Thu, 3 Oct 2024 20:11:21 -0700 Subject: [PATCH 07/31] BFM functioning, main and sub files renamed, needs read packet functionality --- lib/src/interfaces/spi.dart | 15 +++--- lib/src/models/spi_bfm/spi_bfm.dart | 4 +- .../{spi_main.dart => spi_main_agent.dart} | 5 +- lib/src/models/spi_bfm/spi_main_driver.dart | 18 +++---- lib/src/models/spi_bfm/spi_monitor.dart | 36 +++++++++++++- lib/src/models/spi_bfm/spi_packet.dart | 27 ++++++++-- .../{spi_sub.dart => spi_sub_agent.dart} | 4 +- lib/src/models/spi_bfm/spi_sub_driver.dart | 34 +++++-------- lib/src/models/spi_bfm/spi_tracker.dart | 7 +-- test/spi_bfm_test.dart | 49 ++++++++++++------- 10 files changed, 129 insertions(+), 70 deletions(-) rename lib/src/models/spi_bfm/{spi_main.dart => spi_main_agent.dart} (95%) rename lib/src/models/spi_bfm/{spi_sub.dart => spi_sub_agent.dart} (94%) diff --git a/lib/src/interfaces/spi.dart b/lib/src/interfaces/spi.dart index c6a52af9c..3d1f0bcaa 100644 --- a/lib/src/interfaces/spi.dart +++ b/lib/src/interfaces/spi.dart @@ -8,7 +8,6 @@ // Author: Roberto Torres import 'package:rohd/rohd.dart'; -//import 'package:rohd_hcl/src/exceptions.dart'; /// A standard SPI interface. class SpiInterface extends PairInterface { @@ -19,6 +18,9 @@ class SpiInterface extends PairInterface { // CS as individual lines or one // final int csWidth; + // + final int dataLength; + /// Logic get sclk => port('SCLK'); @@ -32,14 +34,15 @@ class SpiInterface extends PairInterface { Logic get cs => port('CSB'); //CS bar /// - SpiInterface() + SpiInterface({this.dataLength = 1}) : super( - portsFromConsumer: [Port('MISO')], - portsFromProvider: [Port('MOSI'), Port('CSB'), Port('SCLK')], - ); + portsFromConsumer: [Port('MISO')], + portsFromProvider: [Port('MOSI'), Port('CSB'), Port('SCLK')]); /// - SpiInterface.clone(SpiInterface super.otherInterface) : super.clone(); + SpiInterface.clone(SpiInterface super.otherInterface) + : dataLength = otherInterface.dataLength, + super.clone(); // multiple CS or 4 bits in parallel } diff --git a/lib/src/models/spi_bfm/spi_bfm.dart b/lib/src/models/spi_bfm/spi_bfm.dart index dc0113d6a..82a48b512 100644 --- a/lib/src/models/spi_bfm/spi_bfm.dart +++ b/lib/src/models/spi_bfm/spi_bfm.dart @@ -7,10 +7,10 @@ // 2024 September 23 // Author: Roberto Torres -export 'spi_main.dart'; +export 'spi_main_agent.dart'; export 'spi_main_driver.dart'; export 'spi_monitor.dart'; export 'spi_packet.dart'; -export 'spi_sub.dart'; +export 'spi_sub_agent.dart'; export 'spi_sub_driver.dart'; export 'spi_tracker.dart'; diff --git a/lib/src/models/spi_bfm/spi_main.dart b/lib/src/models/spi_bfm/spi_main_agent.dart similarity index 95% rename from lib/src/models/spi_bfm/spi_main.dart rename to lib/src/models/spi_bfm/spi_main_agent.dart index 585580169..a44fb15fe 100644 --- a/lib/src/models/spi_bfm/spi_main.dart +++ b/lib/src/models/spi_bfm/spi_main_agent.dart @@ -1,7 +1,7 @@ // Copyright (C) 2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// spi_main.dart +// spi_main_agent.dart // An agent for the main side of the SPI interface. // // 2024 September 23 @@ -12,8 +12,6 @@ import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; /// An agent for the main side of the [SpiInterface]. -/// -/// class SpiMainAgent extends Agent { /// The interface to drive. final SpiInterface intf; @@ -50,6 +48,7 @@ class SpiMainAgent extends Agent { monitor = SpiMonitor( parent: this, + direction: SpiDirection.read, intf: intf, ); } diff --git a/lib/src/models/spi_bfm/spi_main_driver.dart b/lib/src/models/spi_bfm/spi_main_driver.dart index cf71e7d79..d6ed6d631 100644 --- a/lib/src/models/spi_bfm/spi_main_driver.dart +++ b/lib/src/models/spi_bfm/spi_main_driver.dart @@ -57,7 +57,7 @@ class SpiMainDriver extends PendingClockedDriver { /// Drives a packet onto the interface. Future _drivePacket(SpiPacket packet) async { // first, setup - await clk.nextPosedge; + await clk.nextNegedge; // If not selecting this intf, then select it? CS here? // if (!intf.cs.value.toBool()) { // intf.cs.put(0); @@ -69,18 +69,14 @@ class SpiMainDriver extends PendingClockedDriver { // Loop through the bits of the packet for (var i = 0; i < packet.data.width; i++) { - Simulator.injectAction(() { - logger.info('Driving main packet $i'); - intf.mosi.put(packet.data[i]); - intf.sclk.put(1); - }); + logger.info('Driving main packet, index: $i'); + intf.mosi.inject(packet.data[i]); + await clk.nextPosedge; + intf.sclk.inject(1); + // Wait for the next clock cycle await clk.nextNegedge; - Simulator.injectAction(() { - intf.sclk.put(0); - }); - await clk.nextPosedge; + intf.sclk.inject(0); } - // wait for miso to be ready? } } diff --git a/lib/src/models/spi_bfm/spi_monitor.dart b/lib/src/models/spi_bfm/spi_monitor.dart index 1d31de2c9..eb691512f 100644 --- a/lib/src/models/spi_bfm/spi_monitor.dart +++ b/lib/src/models/spi_bfm/spi_monitor.dart @@ -7,6 +7,9 @@ // 2024 September 23 // Author: Roberto Torres +import 'dart:async'; + +import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; @@ -15,11 +18,42 @@ class SpiMonitor extends Monitor { /// The interface to watch. final SpiInterface intf; + /// + final SpiDirection? direction; + /// Creates a new [SpiMonitor] for [intf]. SpiMonitor( {required this.intf, required Component parent, + this.direction, String name = 'spiMonitor'}) : super(name, parent); + + @override + Future run(Phase phase) async { + unawaited(super.run(phase)); + + final dataListRead = []; + final dataListWrite = []; + + intf.sclk.posedge.listen((event) { + if (direction == null || direction == SpiDirection.write) { + dataListWrite.add(intf.mosi.previousValue!); + } + if (direction == null || direction == SpiDirection.read) { + dataListRead.add(intf.miso.previousValue!); + } + + if (dataListWrite.length == intf.dataLength) { + add(SpiPacket( + data: dataListWrite.rswizzle(), direction: SpiDirection.write)); + dataListWrite.clear(); + } + if (dataListRead.length == intf.dataLength) { + add(SpiPacket( + data: dataListRead.rswizzle(), direction: SpiDirection.read)); + dataListRead.clear(); + } + }); + } } -// add switch for mosi vs miso diff --git a/lib/src/models/spi_bfm/spi_packet.dart b/lib/src/models/spi_bfm/spi_packet.dart index 60b58bd8c..1e47a7a85 100644 --- a/lib/src/models/spi_bfm/spi_packet.dart +++ b/lib/src/models/spi_bfm/spi_packet.dart @@ -7,29 +7,46 @@ // 2024 September 23 // Author: Roberto Torres +import 'dart:async'; + import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; +/// s [SpiDirection] [write] f [read] f +enum SpiDirection { write, read } + /// A packet for the [SpiInterface]. class SpiPacket extends SequenceItem implements Trackable { - /// The data in the packet. + /// final LogicValue data; + /// + final SpiDirection? direction; + /// Creates a new packet. - SpiPacket({required this.data}); + SpiPacket({required this.data, this.direction}); + + /// A [Future] that completes once the read has been completed. + Future get completed => _completer.future; + final Completer _completer = Completer(); + + /// Called by a completer when a transfer is completed. + void complete() { + _completer.complete(); + } @override String? trackerString(TrackerField field) { switch (field.title) { case SpiTracker.timeField: return Simulator.time.toString(); + case SpiTracker.typeField: + return direction?.name.substring(0, 1); case SpiTracker.dataField: return data.toString(); } - return trackerString(field); + return null; } } - -// add switch for mosi vs miso \ No newline at end of file diff --git a/lib/src/models/spi_bfm/spi_sub.dart b/lib/src/models/spi_bfm/spi_sub_agent.dart similarity index 94% rename from lib/src/models/spi_bfm/spi_sub.dart rename to lib/src/models/spi_bfm/spi_sub_agent.dart index 3d3587b25..b385a0ffa 100644 --- a/lib/src/models/spi_bfm/spi_sub.dart +++ b/lib/src/models/spi_bfm/spi_sub_agent.dart @@ -1,7 +1,7 @@ // Copyright (C) 2024 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // -// spi_sub.dart +// spi_sub_agent.dart // An agent for the sub side of the SPI interface. // // 2024 September 23 @@ -38,8 +38,10 @@ class SpiSubAgent extends Agent { sequencer: sequencer, ); + /// monitor = SpiMonitor( parent: this, + direction: SpiDirection.write, intf: intf, ); } diff --git a/lib/src/models/spi_bfm/spi_sub_driver.dart b/lib/src/models/spi_bfm/spi_sub_driver.dart index 4fc5b57ec..b33ae04ff 100644 --- a/lib/src/models/spi_bfm/spi_sub_driver.dart +++ b/lib/src/models/spi_bfm/spi_sub_driver.dart @@ -9,7 +9,6 @@ import 'dart:async'; -import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; @@ -32,40 +31,33 @@ class SpiSubDriver extends PendingDriver { Future run(Phase phase) async { unawaited(super.run(phase)); - Simulator.injectAction(() { - intf.miso.inject(0); //high impedance - }); + intf.miso.inject(0); //high impedance? SpiPacket? packet; int? dataIndex; + await intf.cs.nextNegedge; - intf.sclk.posedge.listen((_) { + while (intf.cs.value.isZero) { if (packet == null && pendingSeqItems.isNotEmpty) { packet = pendingSeqItems.removeFirst(); dataIndex = 0; } if (packet != null) { - logger.info('driving data index $dataIndex of sub packet'); - intf.miso.inject(packet!.data[dataIndex!]); - dataIndex = dataIndex! + 1; - if (dataIndex! >= packet!.data.width) { + logger.info('driving sub packet, index: $dataIndex'); + intf.miso.inject(packet.data[dataIndex!]); + dataIndex = dataIndex + 1; + + await intf.sclk.nextNegedge; + + if (dataIndex >= packet.data.width) { packet = null; dataIndex = null; } } else { - intf.miso.inject(0); + intf.miso.inject(0); // high impedance? + break; } - }); + } } - // maybe not necessary - // Simulator.injectAction(() { - // intf.miso.put(0); - //}); - - /// Drives a packet onto the interface. - - // Wait for the next clock cycle - - // wait for miso to be ready? } diff --git a/lib/src/models/spi_bfm/spi_tracker.dart b/lib/src/models/spi_bfm/spi_tracker.dart index 128d17a68..d942e6055 100644 --- a/lib/src/models/spi_bfm/spi_tracker.dart +++ b/lib/src/models/spi_bfm/spi_tracker.dart @@ -18,12 +18,12 @@ class SpiTracker extends Tracker { /// Tracker field for simulation time. static const timeField = 'time'; + /// Tracker field for type (R/W). + static const typeField = 'type'; + /// Tracker field for data. static const dataField = 'data'; - /// Tracker field for CS? - //static const csField = 'csb'; - /// Creates a new tracker for [SpiInterface]. SpiTracker({ required this.intf, @@ -35,6 +35,7 @@ class SpiTracker extends Tracker { int dataColumnWidth = 8, }) : super(name, [ TrackerField(timeField, columnWidth: timeColumnWidth), + const TrackerField(typeField, columnWidth: 1), TrackerField(dataField, columnWidth: dataColumnWidth), ]); } diff --git a/test/spi_bfm_test.dart b/test/spi_bfm_test.dart index 95bef7f68..a22e04c33 100644 --- a/test/spi_bfm_test.dart +++ b/test/spi_bfm_test.dart @@ -8,7 +8,7 @@ // Author: Roberto Torres import 'dart:async'; -import 'dart:convert'; +// import 'dart:convert'; import 'dart:io'; import 'package:rohd/rohd.dart'; @@ -24,13 +24,13 @@ class SpiBfmTest extends Test { late final SpiSubAgent sub; final int numTransfers; - String get outFolder => 'tmp_test/spibfm/$name'; + String get outFolder => 'tmp_test/spibfm/$name/'; SpiBfmTest( super.name, { this.numTransfers = 2, }) : super() { - intf = SpiInterface(); + intf = SpiInterface(dataLength: 8); final clk = SimpleClockGenerator(10).clk; @@ -48,14 +48,14 @@ class SpiBfmTest extends Test { Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); - final jsonStr = - File('$outFolder/spiTracker.tracker.json').readAsStringSync(); - final jsonContents = json.decode(jsonStr); + // final jsonStr = + // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); + // final jsonContents = json.decode(jsonStr); - // ignore: avoid_dynamic_calls - expect(jsonContents['records'].length, 0); + // // ignore: avoid_dynamic_calls + // expect(jsonContents['records'].length, 2); - Directory(outFolder).deleteSync(recursive: true); + //Directory(outFolder).deleteSync(recursive: true); }); monitor.stream.listen(tracker.record); @@ -69,16 +69,35 @@ class SpiBfmTest extends Test { final obj = phase.raiseObjection('spiBfmTestObj'); + // final monitor = SpiMonitor(intf: intf, parent: this); + // final randomData = List.generate(numTransfers, // (index) => LogicValue.ofInt(Test.random!.nextInt(1 << 32), 32)); // for (var i = 0; i < numTransfers; i++) { // final packets = SpiPacket(data: randomData[i]); - main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xCB, 8))); //0b11001011 - sub.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x1A, 8))); //0b00011010 - // numTransfersCompleted++; - // } + main.sequencer.add(SpiPacket( + data: LogicValue.ofInt(0xCB, 8), + direction: SpiDirection.read)); //0b1100 1011 = 203 + + // monitor.stream.listen((data) { + // if (data.direction == SpiDirection.read && + // data.data == LogicValue.ofInt(0xCB, 8)) { + // sub.sequencer.add(SpiPacket( + // data: LogicValue.ofInt(0x1B, 8), + // direction: SpiDirection.read)); //0b0001 1011 = 27 + // } + //}); + + sub.sequencer.add(SpiPacket( + data: LogicValue.ofInt(0x1B, 8), + direction: SpiDirection.read)); //0b0001 1011 = 27 + + // main.sequencer.add(SpiPacket( + // data: LogicValue.ofInt(0x00, 8), + // direction: SpiDirection.read)); //0b0111 0001 = 113 + obj.drop(); } } @@ -94,7 +113,6 @@ void main() { if (dumpWaves) { final mod = SpiMain(spiBfmTest.intf); await mod.build(); - //print(mod.generateSynth()); WaveDumper(mod); } @@ -105,6 +123,3 @@ void main() { await runTest(SpiBfmTest('simple')); }); } - - // test for real use case. 8 bit sent and 8 bit response - From 60511db1b3f030b840fb62b5b522033a4781064e Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Fri, 4 Oct 2024 17:31:05 -0700 Subject: [PATCH 08/31] fixed simple protocol test, first draft of main hardware module --- lib/src/models/spi_bfm/spi_main_agent.dart | 2 +- lib/src/models/spi_bfm/spi_main_driver.dart | 7 ---- lib/src/models/spi_bfm/spi_monitor.dart | 9 +++-- lib/src/models/spi_bfm/spi_packet.dart | 8 ++-- lib/src/models/spi_bfm/spi_sub_agent.dart | 2 +- lib/src/models/spi_bfm/spi_sub_driver.dart | 23 ++++++----- lib/src/models/spi_bfm/spi_tracker.dart | 7 ++-- lib/src/spi_main.dart | 44 +++++++++++++++++++++ test/spi_bfm_test.dart | 44 ++++++++------------- 9 files changed, 90 insertions(+), 56 deletions(-) create mode 100644 lib/src/spi_main.dart diff --git a/lib/src/models/spi_bfm/spi_main_agent.dart b/lib/src/models/spi_bfm/spi_main_agent.dart index a44fb15fe..b2ab63f1b 100644 --- a/lib/src/models/spi_bfm/spi_main_agent.dart +++ b/lib/src/models/spi_bfm/spi_main_agent.dart @@ -48,7 +48,7 @@ class SpiMainAgent extends Agent { monitor = SpiMonitor( parent: this, - direction: SpiDirection.read, + direction: SpiDirection.sub, intf: intf, ); } diff --git a/lib/src/models/spi_bfm/spi_main_driver.dart b/lib/src/models/spi_bfm/spi_main_driver.dart index d6ed6d631..ca39e34da 100644 --- a/lib/src/models/spi_bfm/spi_main_driver.dart +++ b/lib/src/models/spi_bfm/spi_main_driver.dart @@ -56,13 +56,6 @@ class SpiMainDriver extends PendingClockedDriver { /// Drives a packet onto the interface. Future _drivePacket(SpiPacket packet) async { - // first, setup - await clk.nextNegedge; - // If not selecting this intf, then select it? CS here? - // if (!intf.cs.value.toBool()) { - // intf.cs.put(0); - // } - intf.cs.inject(0); // will be extended to multiple CS diff --git a/lib/src/models/spi_bfm/spi_monitor.dart b/lib/src/models/spi_bfm/spi_monitor.dart index eb691512f..d7d5d3569 100644 --- a/lib/src/models/spi_bfm/spi_monitor.dart +++ b/lib/src/models/spi_bfm/spi_monitor.dart @@ -29,6 +29,7 @@ class SpiMonitor extends Monitor { String name = 'spiMonitor'}) : super(name, parent); + /// @override Future run(Phase phase) async { unawaited(super.run(phase)); @@ -37,21 +38,21 @@ class SpiMonitor extends Monitor { final dataListWrite = []; intf.sclk.posedge.listen((event) { - if (direction == null || direction == SpiDirection.write) { + if (direction == null || direction == SpiDirection.main) { dataListWrite.add(intf.mosi.previousValue!); } - if (direction == null || direction == SpiDirection.read) { + if (direction == null || direction == SpiDirection.sub) { dataListRead.add(intf.miso.previousValue!); } if (dataListWrite.length == intf.dataLength) { add(SpiPacket( - data: dataListWrite.rswizzle(), direction: SpiDirection.write)); + data: dataListWrite.rswizzle(), direction: SpiDirection.main)); dataListWrite.clear(); } if (dataListRead.length == intf.dataLength) { add(SpiPacket( - data: dataListRead.rswizzle(), direction: SpiDirection.read)); + data: dataListRead.rswizzle(), direction: SpiDirection.sub)); dataListRead.clear(); } }); diff --git a/lib/src/models/spi_bfm/spi_packet.dart b/lib/src/models/spi_bfm/spi_packet.dart index 1e47a7a85..9032fc011 100644 --- a/lib/src/models/spi_bfm/spi_packet.dart +++ b/lib/src/models/spi_bfm/spi_packet.dart @@ -13,8 +13,9 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; -/// s [SpiDirection] [write] f [read] f -enum SpiDirection { write, read } +/// Doc [main] f +/// Doc [sub] f +enum SpiDirection { main, sub } /// A packet for the [SpiInterface]. class SpiPacket extends SequenceItem implements Trackable { @@ -36,13 +37,14 @@ class SpiPacket extends SequenceItem implements Trackable { _completer.complete(); } + /// @override String? trackerString(TrackerField field) { switch (field.title) { case SpiTracker.timeField: return Simulator.time.toString(); case SpiTracker.typeField: - return direction?.name.substring(0, 1); + return direction?.name; case SpiTracker.dataField: return data.toString(); } diff --git a/lib/src/models/spi_bfm/spi_sub_agent.dart b/lib/src/models/spi_bfm/spi_sub_agent.dart index b385a0ffa..b0fbb825e 100644 --- a/lib/src/models/spi_bfm/spi_sub_agent.dart +++ b/lib/src/models/spi_bfm/spi_sub_agent.dart @@ -41,7 +41,7 @@ class SpiSubAgent extends Agent { /// monitor = SpiMonitor( parent: this, - direction: SpiDirection.write, + direction: SpiDirection.main, intf: intf, ); } diff --git a/lib/src/models/spi_bfm/spi_sub_driver.dart b/lib/src/models/spi_bfm/spi_sub_driver.dart index b33ae04ff..9a19427c4 100644 --- a/lib/src/models/spi_bfm/spi_sub_driver.dart +++ b/lib/src/models/spi_bfm/spi_sub_driver.dart @@ -12,7 +12,7 @@ import 'dart:async'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; -/// A driver for the main side of the [SpiInterface]. +/// A driver for the sub side of the [SpiInterface]. /// /// Driven packets will update the returned data into the same packet. class SpiSubDriver extends PendingDriver { @@ -36,28 +36,33 @@ class SpiSubDriver extends PendingDriver { SpiPacket? packet; int? dataIndex; - await intf.cs.nextNegedge; - while (intf.cs.value.isZero) { + // Function handles the packet. + void packetHandler() { if (packet == null && pendingSeqItems.isNotEmpty) { packet = pendingSeqItems.removeFirst(); dataIndex = 0; } if (packet != null) { logger.info('driving sub packet, index: $dataIndex'); - intf.miso.inject(packet.data[dataIndex!]); - dataIndex = dataIndex + 1; + intf.miso.inject(packet!.data[dataIndex!]); + dataIndex = dataIndex! + 1; - await intf.sclk.nextNegedge; - - if (dataIndex >= packet.data.width) { + if (dataIndex! >= packet!.data.width) { packet = null; dataIndex = null; } } else { intf.miso.inject(0); // high impedance? - break; } } + + intf.cs.negedge.listen((_) { + packetHandler(); + }); + + intf.sclk.negedge.listen((_) { + packetHandler(); + }); } } diff --git a/lib/src/models/spi_bfm/spi_tracker.dart b/lib/src/models/spi_bfm/spi_tracker.dart index d942e6055..1453f3d78 100644 --- a/lib/src/models/spi_bfm/spi_tracker.dart +++ b/lib/src/models/spi_bfm/spi_tracker.dart @@ -18,8 +18,8 @@ class SpiTracker extends Tracker { /// Tracker field for simulation time. static const timeField = 'time'; - /// Tracker field for type (R/W). - static const typeField = 'type'; + /// Tracker field for type from: Main or Sub. + static const typeField = 'from'; /// Tracker field for data. static const dataField = 'data'; @@ -33,9 +33,10 @@ class SpiTracker extends Tracker { super.outputFolder, int timeColumnWidth = 8, int dataColumnWidth = 8, + int typeColumnWidth = 8, }) : super(name, [ TrackerField(timeField, columnWidth: timeColumnWidth), - const TrackerField(typeField, columnWidth: 1), + TrackerField(typeField, columnWidth: typeColumnWidth), TrackerField(dataField, columnWidth: dataColumnWidth), ]); } diff --git a/lib/src/spi_main.dart b/lib/src/spi_main.dart new file mode 100644 index 000000000..445c11b85 --- /dev/null +++ b/lib/src/spi_main.dart @@ -0,0 +1,44 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_main.dart +// Definitions for the SPI interface. +// +// 2024 October 1 +// Author: Roberto Torres + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// Main component for SPI Interface. +class SpiMain extends Module { + /// + SpiMain(Logic bus, SpiInterface intf, + {required Logic clk, required Logic reset, required Logic start}) { + bus = addInput('bus', bus, width: bus.width); + + intf = SpiInterface.clone(intf) + ..pairConnectIO(this, intf, PairRole.provider); + + // Convert Logic bus into a LogicArray of bits + final busArray = LogicArray([bus.width], 1); + for (var i = 0; i < bus.width; i++) { + busArray.elements[i] <= bus[i]; + } + + // + final isRunning = Logic(name: 'isRunning'); + + final serializer = Serializer(busArray, + clk: clk, reset: reset, enable: isRunning, flopInput: true); + + isRunning <= flop(clk, Const(1), en: start, reset: reset | serializer.done); + + intf.sclk <= clk & isRunning; + intf.cs <= ~isRunning; + intf.mosi <= serializer.serialized; + } +} +// shift register for miso + +// Knob for SPI data lenght, SPI mode, CS qty diff --git a/test/spi_bfm_test.dart b/test/spi_bfm_test.dart index a22e04c33..d02f71bfa 100644 --- a/test/spi_bfm_test.dart +++ b/test/spi_bfm_test.dart @@ -22,14 +22,11 @@ class SpiBfmTest extends Test { late final SpiInterface intf; late final SpiMainAgent main; late final SpiSubAgent sub; - final int numTransfers; + late final SpiMonitor monitor; String get outFolder => 'tmp_test/spibfm/$name/'; - SpiBfmTest( - super.name, { - this.numTransfers = 2, - }) : super() { + SpiBfmTest(super.name) : super() { intf = SpiInterface(dataLength: 8); final clk = SimpleClockGenerator(10).clk; @@ -38,7 +35,7 @@ class SpiBfmTest extends Test { sub = SpiSubAgent(intf: intf, parent: this); - final monitor = SpiMonitor(intf: intf, parent: this); + monitor = SpiMonitor(intf: intf, parent: this); Directory(outFolder).createSync(recursive: true); @@ -61,38 +58,29 @@ class SpiBfmTest extends Test { monitor.stream.listen(tracker.record); } - //int numTransfersCompleted = 0; - @override Future run(Phase phase) async { unawaited(super.run(phase)); final obj = phase.raiseObjection('spiBfmTestObj'); - // final monitor = SpiMonitor(intf: intf, parent: this); - - // final randomData = List.generate(numTransfers, - // (index) => LogicValue.ofInt(Test.random!.nextInt(1 << 32), 32)); + main.sequencer + .add(SpiPacket(data: LogicValue.ofInt(0xCB, 8))); //0b1100 1011 = 203 - // for (var i = 0; i < numTransfers; i++) { - // final packets = SpiPacket(data: randomData[i]); + main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x00, 8))); - main.sequencer.add(SpiPacket( - data: LogicValue.ofInt(0xCB, 8), - direction: SpiDirection.read)); //0b1100 1011 = 203 + //main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x00, 8))); - // monitor.stream.listen((data) { - // if (data.direction == SpiDirection.read && - // data.data == LogicValue.ofInt(0xCB, 8)) { - // sub.sequencer.add(SpiPacket( - // data: LogicValue.ofInt(0x1B, 8), - // direction: SpiDirection.read)); //0b0001 1011 = 27 - // } - //}); + unawaited(monitor.stream + .where((event) => + event.direction == SpiDirection.main && event.data.toInt() == 0xCB) + .first + .then((_) { + sub.sequencer + .add(SpiPacket(data: LogicValue.ofInt(0x1B, 8))); //0b0001 1011 = 27 + })); - sub.sequencer.add(SpiPacket( - data: LogicValue.ofInt(0x1B, 8), - direction: SpiDirection.read)); //0b0001 1011 = 27 + // might want a completion // main.sequencer.add(SpiPacket( // data: LogicValue.ofInt(0x00, 8), From f47cc37d363b39b9f76911fc816bdd570dbeac4f Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Tue, 8 Oct 2024 10:19:54 -0700 Subject: [PATCH 09/31] spi main updated, sub intial draft added --- lib/src/spi_main.dart | 23 ++++++++++++++++------- lib/src/spi_sub.dart | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 lib/src/spi_sub.dart diff --git a/lib/src/spi_main.dart b/lib/src/spi_main.dart index 445c11b85..890f139f9 100644 --- a/lib/src/spi_main.dart +++ b/lib/src/spi_main.dart @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause // // spi_main.dart -// Definitions for the SPI interface. +// Implementation of SPI Main component. // // 2024 October 1 // Author: Roberto Torres @@ -13,27 +13,36 @@ import 'package:rohd_hcl/rohd_hcl.dart'; /// Main component for SPI Interface. class SpiMain extends Module { /// - SpiMain(Logic bus, SpiInterface intf, + SpiMain(Logic busIn, Logic busOut, SpiInterface intf, {required Logic clk, required Logic reset, required Logic start}) { - bus = addInput('bus', bus, width: bus.width); + busIn = addInput('bus', busIn, width: busIn.width); + + busOut = addOutput('busOut', width: busOut.width); intf = SpiInterface.clone(intf) ..pairConnectIO(this, intf, PairRole.provider); // Convert Logic bus into a LogicArray of bits - final busArray = LogicArray([bus.width], 1); - for (var i = 0; i < bus.width; i++) { - busArray.elements[i] <= bus[i]; + final busInArray = LogicArray([busIn.width], 1); + for (var i = 0; i < busIn.width; i++) { + busInArray.elements[i] <= busIn[i]; } // final isRunning = Logic(name: 'isRunning'); - final serializer = Serializer(busArray, + // Serializes busInArray + final serializer = Serializer(busInArray, clk: clk, reset: reset, enable: isRunning, flopInput: true); isRunning <= flop(clk, Const(1), en: start, reset: reset | serializer.done); + // Shift register in from MISO + final srMiso = + ShiftRegister(intf.miso, clk: intf.sclk, depth: intf.dataLength); + + busOut <= srMiso.dataOut; + intf.sclk <= clk & isRunning; intf.cs <= ~isRunning; intf.mosi <= serializer.serialized; diff --git a/lib/src/spi_sub.dart b/lib/src/spi_sub.dart new file mode 100644 index 000000000..1f0b00db6 --- /dev/null +++ b/lib/src/spi_sub.dart @@ -0,0 +1,38 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_sub.dart +// Implementation of SPI Sub component. +// +// 2024 October 4 +// Author: Roberto Torres + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// Main component for SPI Interface. +class SpiSub extends Module { + /// + SpiSub(Logic busIn, Logic busOut, Logic reset, SpiInterface intf) { + // SPI Interface + intf = SpiInterface.clone(intf) + ..pairConnectIO(this, intf, PairRole.consumer); + + // Bus Input to Sub + busIn = addInput('busIn', busIn, width: intf.dataLength); + + // Bus Output from Sub + busOut = addOutput('busOut', width: intf.dataLength); + + // Shift Register in from MOSI + final srMosi = ShiftRegister(intf.mosi, + clk: intf.sclk, depth: intf.dataLength, reset: reset); + + // Shift Register out to MISO + final srMiso = ShiftRegister(busIn, + clk: intf.sclk, depth: intf.dataLength, reset: reset); + + intf.miso <= srMiso.dataOut; + busOut <= srMosi.dataOut; + } +} From 5ee62d37988b2a2b79c4cd63c9061c7fee342957 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Wed, 9 Oct 2024 21:04:38 -0700 Subject: [PATCH 10/31] updated shift register with async reset and individual stage resetValue --- doc/components/shift_register.md | 4 +- lib/src/shift_register.dart | 34 ++++++++-- test/shift_register_test.dart | 109 +++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 7 deletions(-) diff --git a/doc/components/shift_register.md b/doc/components/shift_register.md index 634d8ff98..d73918e34 100644 --- a/doc/components/shift_register.md +++ b/doc/components/shift_register.md @@ -5,6 +5,6 @@ The `ShiftRegister` in ROHD-HCL is a configurable shift register including: - support for any width data - a configurable `depth` (which corresponds to the latency) - an optional `enable` -- an optional `reset` -- if `reset` is provided, an optional `resetValue` +- an optional `reset` (synchronous or asynchronous) +- if `reset` is provided, an optional `resetValue` for all stages or each stage indvidually - access to each of the `stages` output from each flop diff --git a/lib/src/shift_register.dart b/lib/src/shift_register.dart index e567ab6d1..7824ad079 100644 --- a/lib/src/shift_register.dart +++ b/lib/src/shift_register.dart @@ -10,6 +10,7 @@ import 'dart:collection'; import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; /// A shift register with configurable width and depth and optional enable and /// reset. @@ -37,14 +38,18 @@ class ShiftRegister extends Module { final String dataName; /// Creates a new shift register with specified [depth] which is only active - /// when [enable]d. If [reset] is provided, it will reset to a default of `0` - /// at all stages synchronously with [clk] or to the provided [resetValue]. + /// when [enable]d. If [reset] is provided, it will reset synchronously with + /// [clk] or aynchronously if [asyncReset] is true. The [reset] will reset all + /// stages to a default of `0` or to the provided [resetValue]. + /// If [resetValue] is a [List] the stages will reset to the corresponding + /// value in the list. ShiftRegister( Logic dataIn, { required Logic clk, required this.depth, Logic? enable, Logic? reset, + bool asyncReset = false, dynamic resetValue, this.dataName = 'data', }) : width = dataIn.width, @@ -55,6 +60,7 @@ class ShiftRegister extends Module { addOutput('${dataName}_out', width: width); Map? resetValues; + if (reset != null) { reset = addInput('reset', reset); @@ -63,6 +69,23 @@ class ShiftRegister extends Module { resetValue = addInput('resetValue', resetValue, width: resetValue.width); } + + if (resetValue is List) { + // Check if list length is equal to depth + if (resetValue.length != depth) { + throw RohdHclException( + 'ResetValue list length must be equal to shift register depth.'); + } + + for (var i = 0; i < resetValue.length; i++) { + final element = resetValue[i]; + if (element is Logic) { + resetValue[i] = + addInput('resetValue$i', element, width: element.width); + } + } + } + resetValues = {}; } } @@ -73,7 +96,8 @@ class ShiftRegister extends Module { for (var i = 0; i < depth; i++) { final stageI = addOutput(_stageName(i), width: width); conds.add(stageI < dataStage); - resetValues?[stageI] = resetValue; + + resetValues?[stageI] = resetValue is List ? resetValue[i] : resetValue; dataStage = stageI; } @@ -83,8 +107,8 @@ class ShiftRegister extends Module { conds = [If(enable, then: conds)]; } - Sequential( - clk, + Sequential.multi( + [clk, if (asyncReset && reset != null) reset], reset: reset, resetValues: resetValues, conds, diff --git a/test/shift_register_test.dart b/test/shift_register_test.dart index eef3ddc48..1887db856 100644 --- a/test/shift_register_test.dart +++ b/test/shift_register_test.dart @@ -209,4 +209,113 @@ void main() { await Simulator.endSimulation(); }); + + group('list reset value shift register', () { + Future listResetTest( + dynamic resetVal, void Function(Logic dataOut) check) async { + final dataIn = Logic(width: 8); + final clk = SimpleClockGenerator(10).clk; + const depth = 5; + final reset = Logic(); + final dataOut = ShiftRegister(dataIn, + clk: clk, depth: depth, reset: reset, resetValue: resetVal) + .dataOut; + + unawaited(Simulator.run()); + + dataIn.put(0x45); + reset.put(true); + + await clk.nextPosedge; + + reset.put(false); + + await clk.waitCycles(3); + + check(dataOut); + + await Simulator.endSimulation(); + } + + test('list of logics reset value', () async { + await listResetTest([ + Logic(width: 8)..put(0x2), + Logic(width: 8)..put(0x10), + Logic(width: 8)..put(0x22), + Logic(width: 8)..put(0x33), + Logic(width: 8)..put(0x42), + ], (dataOut) { + expect(dataOut.value.toInt(), 0x10); + }); + }); + + test('list of mixed reset value', () async { + await listResetTest([ + Logic(width: 8)..put(0x2), + 26, + Logic(width: 8)..put(0x22), + true, + Logic(width: 8)..put(0x42), + ], (dataOut) { + expect(dataOut.value.toInt(), 0x1A); + }); + }); + }); + + group('async reset shift register', () { + Future asyncResetTest( + dynamic resetVal, void Function(Logic dataOut) check) async { + final dataIn = Logic(width: 8); + final clk = SimpleClockGenerator(10).clk; + const depth = 5; + final reset = Logic(); + final dataOut = ShiftRegister(dataIn, + clk: clk, + depth: depth, + reset: reset, + resetValue: resetVal, + asyncReset: true) + .dataOut; + + unawaited(Simulator.run()); + + dataIn.put(0x42); + + reset.inject(false); + + await clk.waitCycles(1); + + reset.inject(true); + + await clk.waitCycles(1); + + check(dataOut); + + await Simulator.endSimulation(); + } + + test('async reset value', () async { + await asyncResetTest(Const(0x78, width: 8), (dataOut) { + expect(dataOut.value.toInt(), 0x78); + }); + }); + + test('async null reset value', () async { + await asyncResetTest(null, (dataOut) { + expect(dataOut.value.toInt(), 0); + }); + }); + + test('async reset with list mixed type', () async { + await asyncResetTest([ + Logic(width: 8)..put(0x2), + 59, + Const(0x78, width: 8), + Logic(width: 8)..put(0x33), + true, + ], (dataOut) { + expect(dataOut.value.toInt(), 0x1); + }); + }); + }); } From 3063f155c6ae1e56a39146e41463c6922947a14a Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Wed, 9 Oct 2024 21:19:45 -0700 Subject: [PATCH 11/31] fixed an exceeds line length --- lib/src/shift_register.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/shift_register.dart b/lib/src/shift_register.dart index 7824ad079..ac4acc268 100644 --- a/lib/src/shift_register.dart +++ b/lib/src/shift_register.dart @@ -74,7 +74,7 @@ class ShiftRegister extends Module { // Check if list length is equal to depth if (resetValue.length != depth) { throw RohdHclException( - 'ResetValue list length must be equal to shift register depth.'); + 'ResetValue list length must equal shift register depth.'); } for (var i = 0; i < resetValue.length; i++) { From c4f626b34d5328206dbc18bab8cced4c138ef1cd Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Thu, 10 Oct 2024 13:06:58 -0700 Subject: [PATCH 12/31] fixed clk tie off in async tests --- test/shift_register_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/shift_register_test.dart b/test/shift_register_test.dart index 1887db856..556c174ef 100644 --- a/test/shift_register_test.dart +++ b/test/shift_register_test.dart @@ -270,7 +270,7 @@ void main() { const depth = 5; final reset = Logic(); final dataOut = ShiftRegister(dataIn, - clk: clk, + clk: Const(0), depth: depth, reset: reset, resetValue: resetVal, From b4c4e52088003f040b89b7218ab21833aed035a2 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Thu, 10 Oct 2024 17:55:35 -0700 Subject: [PATCH 13/31] initial spi components complete, initial testing --- lib/rohd_hcl.dart | 2 + lib/src/{ => gaskets/spi}/spi_main.dart | 19 ++++-- lib/src/gaskets/spi/spi_sub.dart | 52 ++++++++++++++ lib/src/spi_sub.dart | 38 ----------- test/{ => spi}/spi_bfm_test.dart | 17 ++--- test/spi/spi_main_test.dart | 91 +++++++++++++++++++++++++ test/{ => spi}/spi_test.dart | 13 ++-- 7 files changed, 174 insertions(+), 58 deletions(-) rename lib/src/{ => gaskets/spi}/spi_main.dart (71%) create mode 100644 lib/src/gaskets/spi/spi_sub.dart delete mode 100644 lib/src/spi_sub.dart rename test/{ => spi}/spi_bfm_test.dart (85%) create mode 100644 test/spi/spi_main_test.dart rename test/{ => spi}/spi_test.dart (79%) diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index 8cf3540a1..7a599cd1d 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -13,6 +13,8 @@ export 'src/exceptions.dart'; export 'src/extrema.dart'; export 'src/fifo.dart'; export 'src/find.dart'; +export 'src/gaskets/spi/spi_main.dart'; +export 'src/gaskets/spi/spi_sub.dart'; export 'src/interfaces/interfaces.dart'; export 'src/memory/memories.dart'; export 'src/models/models.dart'; diff --git a/lib/src/spi_main.dart b/lib/src/gaskets/spi/spi_main.dart similarity index 71% rename from lib/src/spi_main.dart rename to lib/src/gaskets/spi/spi_main.dart index 890f139f9..39d524508 100644 --- a/lib/src/spi_main.dart +++ b/lib/src/gaskets/spi/spi_main.dart @@ -13,11 +13,14 @@ import 'package:rohd_hcl/rohd_hcl.dart'; /// Main component for SPI Interface. class SpiMain extends Module { /// - SpiMain(Logic busIn, Logic busOut, SpiInterface intf, + Logic get busOut => output('busOut'); + + /// + SpiMain(Logic busIn, SpiInterface intf, {required Logic clk, required Logic reset, required Logic start}) { busIn = addInput('bus', busIn, width: busIn.width); - busOut = addOutput('busOut', width: busOut.width); + addOutput('busOut', width: busIn.width); intf = SpiInterface.clone(intf) ..pairConnectIO(this, intf, PairRole.provider); @@ -35,19 +38,25 @@ class SpiMain extends Module { final serializer = Serializer(busInArray, clk: clk, reset: reset, enable: isRunning, flopInput: true); + // Will run when start is pulsed high, reset on reset or when serializer is done isRunning <= flop(clk, Const(1), en: start, reset: reset | serializer.done); // Shift register in from MISO - final srMiso = + final shiftReg = ShiftRegister(intf.miso, clk: intf.sclk, depth: intf.dataLength); - busOut <= srMiso.dataOut; + // Each busOut bit is connected to the corresponding shift Register stage + busOut <= shiftReg.stages.swizzle(); + // Sclk runs of clk when isRunning is true intf.sclk <= clk & isRunning; + + // CS is active low. It will go low when isRunning is high intf.cs <= ~isRunning; + + // Mosi is connected to the serializer output. intf.mosi <= serializer.serialized; } } -// shift register for miso // Knob for SPI data lenght, SPI mode, CS qty diff --git a/lib/src/gaskets/spi/spi_sub.dart b/lib/src/gaskets/spi/spi_sub.dart new file mode 100644 index 000000000..d036b7ab4 --- /dev/null +++ b/lib/src/gaskets/spi/spi_sub.dart @@ -0,0 +1,52 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_sub.dart +// Implementation of SPI Sub component. +// +// 2024 October 4 +// Author: Roberto Torres + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; + +/// Main component for SPI Interface. +class SpiSub extends Module { + /// + Logic get busOut => output('busOut'); + + /// + SpiSub({required SpiInterface intf, Logic? busIn, Logic? reset}) { + // SPI Interface + intf = SpiInterface.clone(intf) + ..pairConnectIO(this, intf, PairRole.consumer); + + // Bus Input to Sub + if (busIn != null) { + busIn = addInput('busIn', busIn, width: intf.dataLength); + } + + if (reset != null) { + reset = addInput('reset', reset); + } + + // Bus Output from Sub + addOutput('busOut', width: intf.dataLength); + + // Shift Register + final shiftReg = ShiftRegister( + intf.mosi, + clk: intf.sclk & ~intf.cs, + depth: intf.dataLength, + reset: reset, + asyncReset: true, + resetValue: busIn?.elements, + ); + + // BusOut is connected to the stages of the shift register + busOut <= shiftReg.stages.swizzle(); + + // Connect miso to the output of the shift register + intf.miso <= shiftReg.dataOut; + } +} diff --git a/lib/src/spi_sub.dart b/lib/src/spi_sub.dart deleted file mode 100644 index 1f0b00db6..000000000 --- a/lib/src/spi_sub.dart +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: BSD-3-Clause -// -// spi_sub.dart -// Implementation of SPI Sub component. -// -// 2024 October 4 -// Author: Roberto Torres - -import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/rohd_hcl.dart'; - -/// Main component for SPI Interface. -class SpiSub extends Module { - /// - SpiSub(Logic busIn, Logic busOut, Logic reset, SpiInterface intf) { - // SPI Interface - intf = SpiInterface.clone(intf) - ..pairConnectIO(this, intf, PairRole.consumer); - - // Bus Input to Sub - busIn = addInput('busIn', busIn, width: intf.dataLength); - - // Bus Output from Sub - busOut = addOutput('busOut', width: intf.dataLength); - - // Shift Register in from MOSI - final srMosi = ShiftRegister(intf.mosi, - clk: intf.sclk, depth: intf.dataLength, reset: reset); - - // Shift Register out to MISO - final srMiso = ShiftRegister(busIn, - clk: intf.sclk, depth: intf.dataLength, reset: reset); - - intf.miso <= srMiso.dataOut; - busOut <= srMosi.dataOut; - } -} diff --git a/test/spi_bfm_test.dart b/test/spi/spi_bfm_test.dart similarity index 85% rename from test/spi_bfm_test.dart rename to test/spi/spi_bfm_test.dart index d02f71bfa..5ead575d8 100644 --- a/test/spi_bfm_test.dart +++ b/test/spi/spi_bfm_test.dart @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause // // spi_bfm_test.dart -// Definitions for the SPI interface. +// Tests for the SPI BFM. // // 2024 September 23 // Author: Roberto Torres @@ -45,14 +45,15 @@ class SpiBfmTest extends Test { Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); - // final jsonStr = - // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); - // final jsonContents = json.decode(jsonStr); + // // Commented to avoid bug + // final jsonStr = + // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); + // final jsonContents = json.decode(jsonStr); - // // ignore: avoid_dynamic_calls - // expect(jsonContents['records'].length, 2); + // // ignore: avoid_dynamic_calls + // expect(jsonContents['records'].length, 2); - //Directory(outFolder).deleteSync(recursive: true); + // Directory(outFolder).deleteSync(recursive: true); }); monitor.stream.listen(tracker.record); @@ -99,7 +100,7 @@ void main() { Simulator.setMaxSimTime(6000); if (dumpWaves) { - final mod = SpiMain(spiBfmTest.intf); + final mod = SpiMainIntf(spiBfmTest.intf); await mod.build(); WaveDumper(mod); } diff --git a/test/spi/spi_main_test.dart b/test/spi/spi_main_test.dart new file mode 100644 index 000000000..9ff3f6c14 --- /dev/null +++ b/test/spi/spi_main_test.dart @@ -0,0 +1,91 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_main_test.dart +// Definitions for the SPI interface. +// +// 2024 October 10 +// Author: Roberto Torres + +import 'dart:async'; +// import 'dart:convert'; +import 'dart:io'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; +import 'package:test/test.dart'; + +class SpiBfmTest extends Test { + late final SpiInterface intf; + late final SpiMainAgent main; + late final SpiMonitor monitor; + + String get outFolder => 'tmp_test/spibfm/$name/'; + + SpiBfmTest(super.name) : super() { + intf = SpiInterface(dataLength: 8); + + final clk = SimpleClockGenerator(10).clk; + + main = SpiMainAgent(intf: intf, parent: this, clk: clk); + + monitor = SpiMonitor(intf: intf, parent: this); + + Directory(outFolder).createSync(recursive: true); + + final tracker = + SpiTracker(intf: intf, dumpTable: true, outputFolder: outFolder); + + Simulator.registerEndOfSimulationAction(() async { + await tracker.terminate(); + + // final jsonStr = + // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); + // final jsonContents = json.decode(jsonStr); + + // // ignore: avoid_dynamic_calls + // expect(jsonContents['records'].length, 2); + + //Directory(outFolder).deleteSync(recursive: true); + }); + + monitor.stream.listen(tracker.record); + } + + @override + Future run(Phase phase) async { + unawaited(super.run(phase)); + + final obj = phase.raiseObjection('spiBfmTestObj'); + + main.sequencer + .add(SpiPacket(data: LogicValue.ofInt(0xCB, 8))); //0b1100 1011 = 203 + + main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x22, 8))); + + obj.drop(); + } +} + +void main() { + tearDown(() async { + await Simulator.reset(); + }); + + Future runTest(SpiBfmTest spiBfmTest, {bool dumpWaves = true}) async { + Simulator.setMaxSimTime(6000); + final sub = SpiSub(intf: spiBfmTest.intf); + + if (dumpWaves) { + await sub.build(); + WaveDumper(sub); + } + + await spiBfmTest.start(); + } + + test('simple transfers', () async { + await runTest(SpiBfmTest('simple')); + }); +} diff --git a/test/spi_test.dart b/test/spi/spi_test.dart similarity index 79% rename from test/spi_test.dart rename to test/spi/spi_test.dart index 406500c13..4ab962a89 100644 --- a/test/spi_test.dart +++ b/test/spi/spi_test.dart @@ -8,19 +8,18 @@ // Author: Roberto Torres import 'package:rohd/rohd.dart'; -//import 'package:rohd/src/utilities/simcompare.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:test/test.dart'; -class SpiMain extends Module { - SpiMain(SpiInterface intf) { +class SpiMainIntf extends Module { + SpiMainIntf(SpiInterface intf) { intf = SpiInterface.clone(intf) ..pairConnectIO(this, intf, PairRole.provider); } } -class SpiSub extends Module { - SpiSub(SpiInterface intf) { +class SpiSubIntf extends Module { + SpiSubIntf(SpiInterface intf) { intf = SpiInterface.clone(intf) ..pairConnectIO(this, intf, PairRole.consumer); } @@ -29,8 +28,8 @@ class SpiSub extends Module { class SpiTop extends Module { SpiTop() { final intf = SpiInterface(); - SpiMain(intf); - SpiSub(intf); + SpiMainIntf(intf); + SpiSubIntf(intf); addOutput('dummy') <= intf.sclk; } } From 4e99353ef6ddac31a53420a57d357a89f039c465 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Tue, 22 Oct 2024 18:58:37 -0700 Subject: [PATCH 14/31] main and sub tests almost complete. Both gasket tests in progress --- lib/src/gaskets/spi/spi_main.dart | 27 ++- lib/src/models/spi_bfm/spi_sub_driver.dart | 36 ++-- test/spi/spi_bfm_test.dart | 2 - test/spi/spi_gaskets_test.dart | 225 +++++++++++++++++++++ test/spi/spi_main_test.dart | 103 +++++++--- test/spi/spi_sub_test.dart | 91 +++++++++ 6 files changed, 435 insertions(+), 49 deletions(-) create mode 100644 test/spi/spi_gaskets_test.dart create mode 100644 test/spi/spi_sub_test.dart diff --git a/lib/src/gaskets/spi/spi_main.dart b/lib/src/gaskets/spi/spi_main.dart index 39d524508..3866dc22f 100644 --- a/lib/src/gaskets/spi/spi_main.dart +++ b/lib/src/gaskets/spi/spi_main.dart @@ -20,6 +20,12 @@ class SpiMain extends Module { {required Logic clk, required Logic reset, required Logic start}) { busIn = addInput('bus', busIn, width: busIn.width); + clk = addInput('clk', clk); + + reset = addInput('reset', reset); + + start = addInput('start', start); + addOutput('busOut', width: busIn.width); intf = SpiInterface.clone(intf) @@ -33,26 +39,33 @@ class SpiMain extends Module { // final isRunning = Logic(name: 'isRunning'); - - // Serializes busInArray + final done = Logic(name: 'done'); + // Serializes busInArray. final serializer = Serializer(busInArray, - clk: clk, reset: reset, enable: isRunning, flopInput: true); + clk: clk, + reset: reset, + enable: start | (isRunning & ~done), + flopInput: true); + + done <= serializer.done; // Will run when start is pulsed high, reset on reset or when serializer is done - isRunning <= flop(clk, Const(1), en: start, reset: reset | serializer.done); + isRunning <= + flop(clk, Const(1), + en: start, reset: reset | (serializer.done & ~start)); - // Shift register in from MISO + // Shift register in from MISO. final shiftReg = ShiftRegister(intf.miso, clk: intf.sclk, depth: intf.dataLength); // Each busOut bit is connected to the corresponding shift Register stage busOut <= shiftReg.stages.swizzle(); - // Sclk runs of clk when isRunning is true + // Sclk runs off clk when isRunning is true intf.sclk <= clk & isRunning; // CS is active low. It will go low when isRunning is high - intf.cs <= ~isRunning; + intf.cs <= ~(isRunning | start); // Mosi is connected to the serializer output. intf.mosi <= serializer.serialized; diff --git a/lib/src/models/spi_bfm/spi_sub_driver.dart b/lib/src/models/spi_bfm/spi_sub_driver.dart index 9a19427c4..93830a880 100644 --- a/lib/src/models/spi_bfm/spi_sub_driver.dart +++ b/lib/src/models/spi_bfm/spi_sub_driver.dart @@ -13,8 +13,6 @@ import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; /// A driver for the sub side of the [SpiInterface]. -/// -/// Driven packets will update the returned data into the same packet. class SpiSubDriver extends PendingDriver { /// The interface to drive. final SpiInterface intf; @@ -31,38 +29,52 @@ class SpiSubDriver extends PendingDriver { Future run(Phase phase) async { unawaited(super.run(phase)); - intf.miso.inject(0); //high impedance? - + intf.miso.inject(0); SpiPacket? packet; int? dataIndex; // Function handles the packet. - void packetHandler() { + void packetHandler({required bool loadOnly}) { if (packet == null && pendingSeqItems.isNotEmpty) { packet = pendingSeqItems.removeFirst(); - dataIndex = 0; + if (loadOnly) { + dataIndex = 0; + } else { + dataIndex = -1; + } } if (packet != null) { - logger.info('driving sub packet, index: $dataIndex'); - intf.miso.inject(packet!.data[dataIndex!]); - dataIndex = dataIndex! + 1; + if (loadOnly) { + intf.miso.inject(packet!.data[dataIndex!]); + logger.info('injected sub packet, index: $dataIndex'); + } else { + dataIndex = dataIndex! + 1; + logger.info('incremented index to: $dataIndex'); + if (dataIndex! < packet!.data.width) { + logger.info('injecting sub packet, index: $dataIndex'); + intf.miso.inject(packet!.data[dataIndex!]); + } + } if (dataIndex! >= packet!.data.width) { packet = null; dataIndex = null; + packetHandler(loadOnly: loadOnly); } } else { - intf.miso.inject(0); // high impedance? + intf.miso.inject(0); } } intf.cs.negedge.listen((_) { - packetHandler(); + logger.info('cs negedge'); + packetHandler(loadOnly: true); }); intf.sclk.negedge.listen((_) { - packetHandler(); + logger.info('sclk negedge'); + packetHandler(loadOnly: false); }); } } diff --git a/test/spi/spi_bfm_test.dart b/test/spi/spi_bfm_test.dart index 5ead575d8..4f07376c8 100644 --- a/test/spi/spi_bfm_test.dart +++ b/test/spi/spi_bfm_test.dart @@ -81,8 +81,6 @@ class SpiBfmTest extends Test { .add(SpiPacket(data: LogicValue.ofInt(0x1B, 8))); //0b0001 1011 = 27 })); - // might want a completion - // main.sequencer.add(SpiPacket( // data: LogicValue.ofInt(0x00, 8), // direction: SpiDirection.read)); //0b0111 0001 = 113 diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart new file mode 100644 index 000000000..e74dd445b --- /dev/null +++ b/test/spi/spi_gaskets_test.dart @@ -0,0 +1,225 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_gaskets_test.dart +// Tests for SPI gaskets, main and sub. +// +// 2024 October 10 +// Author: Roberto Torres + +import 'dart:async'; +// import 'dart:convert'; +import 'dart:io'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; +import 'package:test/test.dart'; + +class SpiMainTest extends Test { + late final SpiInterface intf; + late final SpiSubAgent sub; + late final SpiMonitor monitor; + late final SpiMain main; + late final Logic reset; + late final Logic starts; + late final Logic clk; + + String get outFolder => 'tmp_test/spiMain/$name/'; + + final Future Function(SpiMainTest test) stimulus; + + SpiMainTest(this.stimulus, super.name) : super() { + intf = SpiInterface(dataLength: 8); + + sub = SpiSubAgent(intf: intf, parent: this); + + monitor = SpiMonitor(intf: intf, parent: this); + + Directory(outFolder).createSync(recursive: true); + + final tracker = + SpiTracker(intf: intf, dumpTable: true, outputFolder: outFolder); + + clk = SimpleClockGenerator(10).clk; + + // initialize the bus with 00 + final busData = Logic(width: 8)..inject(0x00); + reset = Logic(); + starts = Logic(); + + main = SpiMain(busData, intf, clk: clk, reset: reset, start: starts); + + Simulator.registerEndOfSimulationAction(() async { + await tracker.terminate(); + + // // final jsonStr = + // // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); + // // final jsonContents = json.decode(jsonStr); + + // // // ignore: avoid_dynamic_calls + // // expect(jsonContents['records'].length, 2); + + // //Directory(outFolder).deleteSync(recursive: true); + // }); + }); + + monitor.stream.listen(tracker.record); + } + + @override + Future run(Phase phase) async { + unawaited(super.run(phase)); + + final obj = phase.raiseObjection('SpiMainTestObj'); + + // reset flow + reset.inject(true); + starts.inject(false); + await clk.waitCycles(2); + reset.inject(false); + + await stimulus(this); + + obj.drop(); + } +} + +class SpiSubTest extends Test { + late final SpiInterface intf; + late final SpiMainAgent main; + late final SpiMonitor monitor; + late final SpiSub sub; + late final Logic reset; + late final Logic clk; + late final Logic busIn; + + String get outFolder => 'tmp_test/spiSub/$name/'; + + final Future Function(SpiSubTest test) stimulus; + + SpiSubTest(this.stimulus, super.name) : super() { + intf = SpiInterface(dataLength: 8); + + clk = SimpleClockGenerator(10).clk; + + main = SpiMainAgent(intf: intf, parent: this, clk: clk); + + monitor = SpiMonitor(intf: intf, parent: this); + + busIn = Logic(width: 8); + + reset = Logic(); + + sub = SpiSub(intf: intf, busIn: busIn, reset: reset); + + Directory(outFolder).createSync(recursive: true); + + final tracker = + SpiTracker(intf: intf, dumpTable: true, outputFolder: outFolder); + + Simulator.registerEndOfSimulationAction(() async { + await tracker.terminate(); + + // final jsonStr = + // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); + // final jsonContents = json.decode(jsonStr); + + // // ignore: avoid_dynamic_calls + // expect(jsonContents['records'].length, 2); + + //Directory(outFolder).deleteSync(recursive: true); + }); + + monitor.stream.listen(tracker.record); + } + + @override + Future run(Phase phase) async { + unawaited(super.run(phase)); + + final obj = phase.raiseObjection('SpiSubTestObj'); + + // reset flow + reset.inject(true); + await clk.waitCycles(2); + reset.inject(false); + + await stimulus(this); + + obj.drop(); + } +} + +void main() { + tearDown(() async { + await Simulator.reset(); + }); + group('sub gasket tests', () { + Future runSubTest(SpiSubTest spiSubTest, + {bool dumpWaves = true}) async { + Simulator.setMaxSimTime(6000); + + if (dumpWaves) { + await spiSubTest.sub.build(); + WaveDumper(spiSubTest.sub); + } + + await spiSubTest.start(); + } + + test('simple sub transfers', () async { + await runSubTest(SpiSubTest((test) async { + test.main.sequencer.add( + SpiPacket(data: LogicValue.ofInt(0xCB, 8))); //0b1100 1011 = 203 + + test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x22, 8))); + }, 'testSubA')); + }); + }); + group('main gasket tests', () { + Future runMainTest(SpiMainTest spiMainTest, + {bool dumpWaves = true}) async { + Simulator.setMaxSimTime(6000); + + if (dumpWaves) { + await spiMainTest.main.build(); + WaveDumper(spiMainTest.main); + } + await spiMainTest.start(); + } + + Future sendPacket(SpiMainTest test, LogicValue data) async { + test.sub.sequencer.add(SpiPacket(data: data)); + test.starts.inject(true); + await test.clk.waitCycles(1); + test.starts.inject(false); + await test.clk.waitCycles(7); + } + + test('simple transfers no gap', () async { + await runMainTest(SpiMainTest((test) async { + await sendPacket(test, LogicValue.ofInt(0x72, 8)); + expect(test.main.busOut.value.toInt(), 0x72); + await sendPacket(test, LogicValue.ofInt(0xCD, 8)); + expect(test.main.busOut.value.toInt(), 0xCD); + await sendPacket(test, LogicValue.ofInt(0x56, 8)); + expect(test.main.busOut.value.toInt(), 0x56); + }, 'testMainA')); + }); + + test('simple transfers with gaps', () async { + await runMainTest(SpiMainTest((test) async { + await sendPacket(test, LogicValue.ofInt(0x72, 8)); + await test.clk.waitCycles(1); + expect(test.main.busOut.value.toInt(), 0x72); + await sendPacket(test, LogicValue.ofInt(0xCD, 8)); + await test.clk.waitCycles(4); + expect(test.main.busOut.value.toInt(), 0xCD); + await sendPacket(test, LogicValue.ofInt(0x56, 8)); + await test.clk.waitCycles(1); + expect(test.main.busOut.value.toInt(), 0x56); + }, 'testMainB')); + }); + }); +} diff --git a/test/spi/spi_main_test.dart b/test/spi/spi_main_test.dart index 9ff3f6c14..017c3cee0 100644 --- a/test/spi/spi_main_test.dart +++ b/test/spi/spi_main_test.dart @@ -2,7 +2,7 @@ // SPDX-License-Identifier: BSD-3-Clause // // spi_main_test.dart -// Definitions for the SPI interface. +// Tests for SPI main gasket. // // 2024 October 10 // Author: Roberto Torres @@ -16,19 +16,23 @@ import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; import 'package:test/test.dart'; -class SpiBfmTest extends Test { +class SpiMainTest extends Test { late final SpiInterface intf; - late final SpiMainAgent main; + late final SpiSubAgent sub; late final SpiMonitor monitor; + late final SpiMain main; + late final Logic reset; + late final Logic starts; + late final Logic clk; - String get outFolder => 'tmp_test/spibfm/$name/'; + String get outFolder => 'tmp_test/spiMain/$name/'; - SpiBfmTest(super.name) : super() { - intf = SpiInterface(dataLength: 8); + final Future Function(SpiMainTest test) stimulus; - final clk = SimpleClockGenerator(10).clk; + SpiMainTest(this.stimulus, super.name) : super() { + intf = SpiInterface(dataLength: 8); - main = SpiMainAgent(intf: intf, parent: this, clk: clk); + sub = SpiSubAgent(intf: intf, parent: this); monitor = SpiMonitor(intf: intf, parent: this); @@ -37,17 +41,27 @@ class SpiBfmTest extends Test { final tracker = SpiTracker(intf: intf, dumpTable: true, outputFolder: outFolder); + clk = SimpleClockGenerator(10).clk; + + // initialize the bus with 00 + final busData = Logic(width: 8)..inject(0x00); + reset = Logic(); + starts = Logic(); + + main = SpiMain(busData, intf, clk: clk, reset: reset, start: starts); + Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); - // final jsonStr = - // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); - // final jsonContents = json.decode(jsonStr); + // // final jsonStr = + // // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); + // // final jsonContents = json.decode(jsonStr); - // // ignore: avoid_dynamic_calls - // expect(jsonContents['records'].length, 2); + // // // ignore: avoid_dynamic_calls + // // expect(jsonContents['records'].length, 2); - //Directory(outFolder).deleteSync(recursive: true); + // //Directory(outFolder).deleteSync(recursive: true); + // }); }); monitor.stream.listen(tracker.record); @@ -57,12 +71,15 @@ class SpiBfmTest extends Test { Future run(Phase phase) async { unawaited(super.run(phase)); - final obj = phase.raiseObjection('spiBfmTestObj'); + final obj = phase.raiseObjection('SpiMainTestObj'); - main.sequencer - .add(SpiPacket(data: LogicValue.ofInt(0xCB, 8))); //0b1100 1011 = 203 + // reset flow + reset.inject(true); + starts.inject(false); + await clk.waitCycles(2); + reset.inject(false); - main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x22, 8))); + await stimulus(this); obj.drop(); } @@ -73,19 +90,49 @@ void main() { await Simulator.reset(); }); - Future runTest(SpiBfmTest spiBfmTest, {bool dumpWaves = true}) async { - Simulator.setMaxSimTime(6000); - final sub = SpiSub(intf: spiBfmTest.intf); + group('main gasket tests', () { + Future runMainTest(SpiMainTest spiMainTest, + {bool dumpWaves = true}) async { + Simulator.setMaxSimTime(6000); - if (dumpWaves) { - await sub.build(); - WaveDumper(sub); + if (dumpWaves) { + await spiMainTest.main.build(); + WaveDumper(spiMainTest.main); + } + await spiMainTest.start(); } - await spiBfmTest.start(); - } + Future sendPacket(SpiMainTest test, LogicValue data) async { + test.sub.sequencer.add(SpiPacket(data: data)); + test.starts.inject(true); + await test.clk.waitCycles(1); + test.starts.inject(false); + await test.clk.waitCycles(7); + } - test('simple transfers', () async { - await runTest(SpiBfmTest('simple')); + test('simple transfers no gap', () async { + await runMainTest(SpiMainTest((test) async { + await sendPacket(test, LogicValue.ofInt(0x72, 8)); + expect(test.main.busOut.value.toInt(), 0x72); + await sendPacket(test, LogicValue.ofInt(0xCD, 8)); + expect(test.main.busOut.value.toInt(), 0xCD); + await sendPacket(test, LogicValue.ofInt(0x56, 8)); + expect(test.main.busOut.value.toInt(), 0x56); + }, 'testMainA')); + }); + + test('simple transfers with gaps', () async { + await runMainTest(SpiMainTest((test) async { + await sendPacket(test, LogicValue.ofInt(0x72, 8)); + await test.clk.waitCycles(1); + expect(test.main.busOut.value.toInt(), 0x72); + await sendPacket(test, LogicValue.ofInt(0xCD, 8)); + await test.clk.waitCycles(4); + expect(test.main.busOut.value.toInt(), 0xCD); + await sendPacket(test, LogicValue.ofInt(0x56, 8)); + await test.clk.waitCycles(1); + expect(test.main.busOut.value.toInt(), 0x56); + }, 'testMainB')); + }); }); } diff --git a/test/spi/spi_sub_test.dart b/test/spi/spi_sub_test.dart new file mode 100644 index 000000000..272ef4eea --- /dev/null +++ b/test/spi/spi_sub_test.dart @@ -0,0 +1,91 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_sub_test.dart +// Tests for SPI Sub gasket. +// +// 2024 October 10 +// Author: Roberto Torres + +import 'dart:async'; +// import 'dart:convert'; +import 'dart:io'; + +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; +import 'package:test/test.dart'; + +class SpiSubTest extends Test { + late final SpiInterface intf; + late final SpiMainAgent main; + late final SpiMonitor monitor; + + String get outFolder => 'tmp_test/spibfm/$name/'; + + SpiSubTest(super.name) : super() { + intf = SpiInterface(dataLength: 8); + + final clk = SimpleClockGenerator(10).clk; + + main = SpiMainAgent(intf: intf, parent: this, clk: clk); + + monitor = SpiMonitor(intf: intf, parent: this); + + Directory(outFolder).createSync(recursive: true); + + final tracker = + SpiTracker(intf: intf, dumpTable: true, outputFolder: outFolder); + + Simulator.registerEndOfSimulationAction(() async { + await tracker.terminate(); + + // final jsonStr = + // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); + // final jsonContents = json.decode(jsonStr); + + // // ignore: avoid_dynamic_calls + // expect(jsonContents['records'].length, 2); + + //Directory(outFolder).deleteSync(recursive: true); + }); + + monitor.stream.listen(tracker.record); + } + + @override + Future run(Phase phase) async { + unawaited(super.run(phase)); + + final obj = phase.raiseObjection('SpiSubTestObj'); + + main.sequencer + .add(SpiPacket(data: LogicValue.ofInt(0xCB, 8))); //0b1100 1011 = 203 + + main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x22, 8))); + + obj.drop(); + } +} + +void main() { + tearDown(() async { + await Simulator.reset(); + }); + + Future runTest(SpiSubTest spiSubTest, {bool dumpWaves = true}) async { + Simulator.setMaxSimTime(6000); + final sub = SpiSub(intf: spiSubTest.intf); + + if (dumpWaves) { + await sub.build(); + WaveDumper(sub); + } + + await spiSubTest.start(); + } + + test('simple transfers', () async { + await runTest(SpiSubTest('simple')); + }); +} From 242bdbbef20499e2de0365e67a419099051b2adf Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Fri, 25 Oct 2024 14:19:57 -0700 Subject: [PATCH 15/31] main gasket tests good, sub has cycle delay, pair in progress --- lib/src/gaskets/spi/spi_main.dart | 20 ++- lib/src/gaskets/spi/spi_sub.dart | 13 +- lib/src/shift_register.dart | 2 + test/spi/spi_gaskets_test.dart | 243 +++++++++++++++++++++++++----- test/spi/spi_test.dart | 6 +- 5 files changed, 235 insertions(+), 49 deletions(-) diff --git a/lib/src/gaskets/spi/spi_main.dart b/lib/src/gaskets/spi/spi_main.dart index 3866dc22f..c3253f422 100644 --- a/lib/src/gaskets/spi/spi_main.dart +++ b/lib/src/gaskets/spi/spi_main.dart @@ -15,10 +15,16 @@ class SpiMain extends Module { /// Logic get busOut => output('busOut'); + /// + Logic get done => output('done'); + /// SpiMain(Logic busIn, SpiInterface intf, - {required Logic clk, required Logic reset, required Logic start}) { - busIn = addInput('bus', busIn, width: busIn.width); + {required Logic clk, + required Logic reset, + required Logic start, + super.name = 'spiMain'}) { + busIn = addInput('busIn', busIn, width: busIn.width); clk = addInput('clk', clk); @@ -28,6 +34,8 @@ class SpiMain extends Module { addOutput('busOut', width: busIn.width); + addOutput('done'); + intf = SpiInterface.clone(intf) ..pairConnectIO(this, intf, PairRole.provider); @@ -39,7 +47,7 @@ class SpiMain extends Module { // final isRunning = Logic(name: 'isRunning'); - final done = Logic(name: 'done'); + // Serializes busInArray. final serializer = Serializer(busInArray, clk: clk, @@ -47,13 +55,13 @@ class SpiMain extends Module { enable: start | (isRunning & ~done), flopInput: true); - done <= serializer.done; - // Will run when start is pulsed high, reset on reset or when serializer is done isRunning <= flop(clk, Const(1), en: start, reset: reset | (serializer.done & ~start)); + done <= serializer.done; + // Shift register in from MISO. final shiftReg = ShiftRegister(intf.miso, clk: intf.sclk, depth: intf.dataLength); @@ -68,7 +76,7 @@ class SpiMain extends Module { intf.cs <= ~(isRunning | start); // Mosi is connected to the serializer output. - intf.mosi <= serializer.serialized; + intf.mosi <= flop(~intf.sclk, serializer.serialized); } } diff --git a/lib/src/gaskets/spi/spi_sub.dart b/lib/src/gaskets/spi/spi_sub.dart index d036b7ab4..b1afce34f 100644 --- a/lib/src/gaskets/spi/spi_sub.dart +++ b/lib/src/gaskets/spi/spi_sub.dart @@ -10,22 +10,27 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -/// Main component for SPI Interface. +/// Sub component for SPI Interface. class SpiSub extends Module { /// Logic get busOut => output('busOut'); /// - SpiSub({required SpiInterface intf, Logic? busIn, Logic? reset}) { + SpiSub( + {required SpiInterface intf, + Logic? busIn, + Logic? reset, + super.name = 'spiSub'}) { // SPI Interface intf = SpiInterface.clone(intf) ..pairConnectIO(this, intf, PairRole.consumer); - // Bus Input to Sub + // Bus Input to sub, if provided. if (busIn != null) { busIn = addInput('busIn', busIn, width: intf.dataLength); } + // Reset signal for sub, if provided. if (reset != null) { reset = addInput('reset', reset); } @@ -47,6 +52,6 @@ class SpiSub extends Module { busOut <= shiftReg.stages.swizzle(); // Connect miso to the output of the shift register - intf.miso <= shiftReg.dataOut; + intf.miso <= flop(~intf.sclk, shiftReg.dataOut); } } diff --git a/lib/src/shift_register.dart b/lib/src/shift_register.dart index ac4acc268..acfcad814 100644 --- a/lib/src/shift_register.dart +++ b/lib/src/shift_register.dart @@ -77,6 +77,8 @@ class ShiftRegister extends Module { 'ResetValue list length must equal shift register depth.'); } + resetValue = List.of(resetValue); + for (var i = 0; i < resetValue.length; i++) { final element = resetValue[i]; if (element is Logic) { diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index e74dd445b..de72bc33b 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -76,9 +76,9 @@ class SpiMainTest extends Test { // reset flow reset.inject(true); starts.inject(false); - await clk.waitCycles(2); + await clk.waitCycles(1); reset.inject(false); - + //await clk.waitCycles(2); await stimulus(this); obj.drop(); @@ -142,54 +142,122 @@ class SpiSubTest extends Test { // reset flow reset.inject(true); - await clk.waitCycles(2); + await clk.waitCycles(1); reset.inject(false); - + //await clk.waitCycles(1); await stimulus(this); obj.drop(); } } -void main() { - tearDown(() async { - await Simulator.reset(); - }); - group('sub gasket tests', () { - Future runSubTest(SpiSubTest spiSubTest, - {bool dumpWaves = true}) async { - Simulator.setMaxSimTime(6000); +class SpiTop extends Module { + SpiTop(SpiInterface intf, SpiSubTest? test, {super.name = 'spiTop'}) { + addOutput('dummy') <= intf.sclk; + if (test != null) { + addOutput('clk') <= test.clk; + } + } +} - if (dumpWaves) { - await spiSubTest.sub.build(); - WaveDumper(spiSubTest.sub); - } +class SpiPairTest extends Test { + late final SpiInterface intf; + late final SpiMonitor monitor; + late final SpiMain main; + late final SpiSub sub; + late final Logic clk; + late final Logic resetMain; + late final Logic resetSub; + late final Logic busInMain; + late final Logic busInSub; + late final Logic starts; - await spiSubTest.start(); - } + String get outFolder => 'tmp_test/spiPair/$name/'; - test('simple sub transfers', () async { - await runSubTest(SpiSubTest((test) async { - test.main.sequencer.add( - SpiPacket(data: LogicValue.ofInt(0xCB, 8))); //0b1100 1011 = 203 + final Future Function(SpiPairTest test) stimulus; - test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x22, 8))); - }, 'testSubA')); + SpiPairTest(this.stimulus, super.name) : super() { + intf = SpiInterface(dataLength: 8); + + monitor = SpiMonitor(intf: intf, parent: this); + + Directory(outFolder).createSync(recursive: true); + + final tracker = + SpiTracker(intf: intf, dumpTable: true, outputFolder: outFolder); + + clk = SimpleClockGenerator(10).clk; + + // initialize main + resetMain = Logic(); + busInMain = Logic(width: 8); + starts = Logic(); + + main = SpiMain(busInMain, intf, clk: clk, reset: resetMain, start: starts); + + //init sub + resetSub = Logic(); + busInSub = Logic(width: 8); + sub = SpiSub(intf: intf, busIn: busInSub, reset: resetSub); + + Simulator.registerEndOfSimulationAction(() async { + await tracker.terminate(); + + // // final jsonStr = + // // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); + // // final jsonContents = json.decode(jsonStr); + + // // // ignore: avoid_dynamic_calls + // // expect(jsonContents['records'].length, 2); + + // //Directory(outFolder).deleteSync(recursive: true); + // }); }); + + monitor.stream.listen(tracker.record); + } + + @override + Future run(Phase phase) async { + unawaited(super.run(phase)); + + final obj = phase.raiseObjection('SpiPairTestObj'); + + // reset flow + resetMain.inject(true); + resetSub.inject(true); + starts.inject(false); + // extra cycles for easy waveform visibility + await clk.waitCycles(3); + resetMain.inject(false); + resetSub.inject(false); + busInSub.inject(00); + + await stimulus(this); + + obj.drop(); + } +} + +void main() { + tearDown(() async { + await Simulator.reset(); }); + group('main gasket tests', () { Future runMainTest(SpiMainTest spiMainTest, {bool dumpWaves = true}) async { - Simulator.setMaxSimTime(6000); + Simulator.setMaxSimTime(3000); if (dumpWaves) { await spiMainTest.main.build(); - WaveDumper(spiMainTest.main); + WaveDumper(spiMainTest.main, + outputPath: '${spiMainTest.outFolder}/waves.vcd'); } await spiMainTest.start(); } - Future sendPacket(SpiMainTest test, LogicValue data) async { + Future sendSubPacket(SpiMainTest test, LogicValue data) async { test.sub.sequencer.add(SpiPacket(data: data)); test.starts.inject(true); await test.clk.waitCycles(1); @@ -199,27 +267,130 @@ void main() { test('simple transfers no gap', () async { await runMainTest(SpiMainTest((test) async { - await sendPacket(test, LogicValue.ofInt(0x72, 8)); + await sendSubPacket(test, LogicValue.ofInt(0x72, 8)); expect(test.main.busOut.value.toInt(), 0x72); - await sendPacket(test, LogicValue.ofInt(0xCD, 8)); + + // await test.clk.nextNegedge; + await sendSubPacket(test, LogicValue.ofInt(0xCD, 8)); expect(test.main.busOut.value.toInt(), 0xCD); - await sendPacket(test, LogicValue.ofInt(0x56, 8)); + + // await test.clk.nextNegedge; + await sendSubPacket(test, LogicValue.ofInt(0x56, 8)); expect(test.main.busOut.value.toInt(), 0x56); + + // await test.clk.nextNegedge; + await sendSubPacket(test, LogicValue.ofInt(0xE2, 8)); + expect(test.main.busOut.value.toInt(), 0xE2); + await test.clk.waitCycles(4); }, 'testMainA')); }); test('simple transfers with gaps', () async { await runMainTest(SpiMainTest((test) async { - await sendPacket(test, LogicValue.ofInt(0x72, 8)); - await test.clk.waitCycles(1); + await sendSubPacket(test, LogicValue.ofInt(0x72, 8)); + expect(test.main.busOut.value.toInt(), 0x72); - await sendPacket(test, LogicValue.ofInt(0xCD, 8)); - await test.clk.waitCycles(4); + await test.clk.waitCycles(3); + + await sendSubPacket(test, LogicValue.ofInt(0xCD, 8)); // 1100 1101 + expect(test.main.busOut.value.toInt(), 0xCD); - await sendPacket(test, LogicValue.ofInt(0x56, 8)); - await test.clk.waitCycles(1); + await test.clk.waitCycles(4); + + await sendSubPacket(test, LogicValue.ofInt(0x56, 8)); + expect(test.main.busOut.value.toInt(), 0x56); + await test.clk.waitCycles(4); }, 'testMainB')); }); }); + + group('sub gasket tests', () { + Future runSubTest(SpiSubTest spiSubTest, + {bool dumpWaves = true}) async { + Simulator.setMaxSimTime(6000); + final mod = SpiTop(spiSubTest.intf, spiSubTest); + if (dumpWaves) { + await mod.build(); + WaveDumper(mod, outputPath: '${spiSubTest.outFolder}/waves.vcd'); + } + + await spiSubTest.start(); + } + + test('sub tx with busIn and reset', () async { + await runSubTest(SpiSubTest((test) async { + test.main.sequencer + .add(SpiPacket(data: LogicValue.ofInt(0xCD, 8))); // 1100 1101 + test.main.sequencer + .add(SpiPacket(data: LogicValue.ofInt(0x2E, 8))); // 0010 1110 + await test.intf.sclk.waitCycles(16); + await test.clk.waitCycles(1); + test.busIn.inject(0x19); // 0001 1001 need to change to LSB. + test.reset.inject(true); + await test.clk.nextChanged; + test.reset.inject(false); + + await test.clk.waitCycles(1); + test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x00, 8))); + await test.clk.waitCycles(10); + }, 'testSubA')); + }); + }); + + group('pair of gaskets tests', () { + Future runPairTest(SpiPairTest spiPairTest, + {bool dumpWaves = true}) async { + Simulator.setMaxSimTime(6000); + final mod = SpiTop(spiPairTest.intf, null); + if (dumpWaves) { + await mod.build(); + WaveDumper(mod, outputPath: '${spiPairTest.outFolder}/waves.vcd'); + } + await spiPairTest.start(); + } + + Future sendMainPacket(SpiPairTest test, LogicValue data) async { + test.busInMain.inject(data); + test.starts.inject(true); + await test.clk.waitCycles(1); + test.starts.inject(false); + await test.clk.waitCycles(7); + } + + Future sendSubPacket(SpiPairTest test, LogicValue data) async { + test.busInSub.inject(data); + test.starts.inject(true); + await test.clk.waitCycles(1); + test.starts.inject(false); + await test.clk.waitCycles(7); + } + + test('simple transfers no gap', () async { + await runPairTest(SpiPairTest((test) async { + await sendMainPacket(test, LogicValue.ofInt(0x72, 8)); // 0111 0010 + await sendMainPacket(test, LogicValue.ofInt(0x00, 8)); // 0111 0010 + expect(test.sub.busOut.value.toInt(), 0x72); + await test.clk.waitCycles(2); + // await sendMainPacket(test, LogicValue.ofInt(0xCD, 8)); + // expect(test.main.busOut.value.toInt(), 0xCD); + // await sendMainPacket(test, LogicValue.ofInt(0x56, 8)); + // expect(test.main.busOut.value.toInt(), 0x56); + }, 'testPairA')); + }); + + // test('simple transfers with gaps', () async { + // await runBothTest(SpiBothTest((test) async { + // await sendMainPacket(test, LogicValue.ofInt(0x72, 8)); + // await test.clk.waitCycles(1); + // expect(test.main.busOut.value.toInt(), 0x72); + // await sendMainPacket(test, LogicValue.ofInt(0xCD, 8)); + // await test.clk.waitCycles(4); + // expect(test.main.busOut.value.toInt(), 0xCD); + // await sendMainPacket(test, LogicValue.ofInt(0x56, 8)); + // await test.clk.waitCycles(1); + // expect(test.main.busOut.value.toInt(), 0x56); + // }, 'testPairB')); + // }); + }); } diff --git a/test/spi/spi_test.dart b/test/spi/spi_test.dart index 4ab962a89..944b2c509 100644 --- a/test/spi/spi_test.dart +++ b/test/spi/spi_test.dart @@ -25,8 +25,8 @@ class SpiSubIntf extends Module { } } -class SpiTop extends Module { - SpiTop() { +class SpiTopIntf extends Module { + SpiTopIntf() { final intf = SpiInterface(); SpiMainIntf(intf); SpiSubIntf(intf); @@ -40,7 +40,7 @@ void main() { }); test('spi_test', () async { - final mod = SpiTop(); + final mod = SpiTopIntf(); await mod.build(); //print(mod.generateSynth()); }); From 3cf5cfc84550cc2d2d7ec12c08c0a92d09eb926a Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Fri, 20 Dec 2024 10:17:27 -0800 Subject: [PATCH 16/31] pair gaskets working, bfms wip --- lib/src/gaskets/spi/spi_main.dart | 70 ++-- lib/src/gaskets/spi/spi_sub.dart | 18 +- lib/src/interfaces/spi.dart | 27 +- lib/src/models/spi_bfm/spi_main_driver.dart | 47 ++- lib/src/models/spi_bfm/spi_sub_driver.dart | 2 +- pubspec.yaml | 6 + test/spi/spi_bfm_test.dart | 16 +- test/spi/spi_gaskets_test.dart | 361 ++++++++++++++++---- test/spi/spi_main_test.dart | 2 +- test/spi/spi_test.dart | 6 +- 10 files changed, 420 insertions(+), 135 deletions(-) diff --git a/lib/src/gaskets/spi/spi_main.dart b/lib/src/gaskets/spi/spi_main.dart index c3253f422..c7901fd01 100644 --- a/lib/src/gaskets/spi/spi_main.dart +++ b/lib/src/gaskets/spi/spi_main.dart @@ -12,17 +12,18 @@ import 'package:rohd_hcl/rohd_hcl.dart'; /// Main component for SPI Interface. class SpiMain extends Module { - /// + /// Output bus from Main. Logic get busOut => output('busOut'); - /// + /// Done signal from Main. Logic get done => output('done'); - /// - SpiMain(Logic busIn, SpiInterface intf, + /// Constructs a SPI Main component. + SpiMain(SpiInterface intf, {required Logic clk, required Logic reset, required Logic start, + required Logic busIn, super.name = 'spiMain'}) { busIn = addInput('busIn', busIn, width: busIn.width); @@ -39,45 +40,52 @@ class SpiMain extends Module { intf = SpiInterface.clone(intf) ..pairConnectIO(this, intf, PairRole.provider); - // Convert Logic bus into a LogicArray of bits - final busInArray = LogicArray([busIn.width], 1); - for (var i = 0; i < busIn.width; i++) { - busInArray.elements[i] <= busIn[i]; - } - - // final isRunning = Logic(name: 'isRunning'); - // Serializes busInArray. - final serializer = Serializer(busInArray, - clk: clk, + final count = Counter.simple( + clk: ~clk, + // enable: start | (isRunning & ~done), + enable: start | isRunning, reset: reset, - enable: start | (isRunning & ~done), - flopInput: true); + minValue: 1, + maxValue: busIn.width); - // Will run when start is pulsed high, reset on reset or when serializer is done + // done <= flop(~clk, count.equalsMax, reset: reset, asyncReset: true); + done <= count.equalsMax; + // Will run when start is pulsed high, reset on reset or when serializer is + // done and start is low. isRunning <= - flop(clk, Const(1), - en: start, reset: reset | (serializer.done & ~start)); - - done <= serializer.done; + flop( + clk, + // start | (isRunning & ~done), + start | ~done, + // en: start, + reset: reset, + asyncReset: true, + ); // Shift register in from MISO. - final shiftReg = - ShiftRegister(intf.miso, clk: intf.sclk, depth: intf.dataLength); + final shiftReg = ShiftRegister( + intf.miso, + clk: intf.sclk, + depth: intf.dataLength, + reset: reset, + asyncReset: true, + resetValue: busIn.elements, + ); // Each busOut bit is connected to the corresponding shift Register stage - busOut <= shiftReg.stages.swizzle(); + busOut <= shiftReg.stages.rswizzle(); - // Sclk runs off clk when isRunning is true - intf.sclk <= clk & isRunning; + // Sclk runs off clk when isRunning is true or start is pulsed high. + intf.sclk <= ~clk & (isRunning | start); - // CS is active low. It will go low when isRunning is high - intf.cs <= ~(isRunning | start); + // CS is active low. It will go low when isRunning or start is pulsed high. + intf.csb <= ~(isRunning | start); // Mosi is connected to the serializer output. - intf.mosi <= flop(~intf.sclk, serializer.serialized); + intf.mosi <= + flop(~intf.sclk, shiftReg.dataOut, + reset: reset, asyncReset: true, resetValue: busIn[-1]); } } - -// Knob for SPI data lenght, SPI mode, CS qty diff --git a/lib/src/gaskets/spi/spi_sub.dart b/lib/src/gaskets/spi/spi_sub.dart index b1afce34f..a8e47ca57 100644 --- a/lib/src/gaskets/spi/spi_sub.dart +++ b/lib/src/gaskets/spi/spi_sub.dart @@ -12,7 +12,7 @@ import 'package:rohd_hcl/rohd_hcl.dart'; /// Sub component for SPI Interface. class SpiSub extends Module { - /// + /// Output bus from Sub. Logic get busOut => output('busOut'); /// @@ -31,6 +31,7 @@ class SpiSub extends Module { } // Reset signal for sub, if provided. + // will need to be toggled to load new busIn values if (reset != null) { reset = addInput('reset', reset); } @@ -41,7 +42,8 @@ class SpiSub extends Module { // Shift Register final shiftReg = ShiftRegister( intf.mosi, - clk: intf.sclk & ~intf.cs, + enable: ~intf.csb, + clk: intf.sclk, depth: intf.dataLength, reset: reset, asyncReset: true, @@ -49,9 +51,13 @@ class SpiSub extends Module { ); // BusOut is connected to the stages of the shift register - busOut <= shiftReg.stages.swizzle(); - - // Connect miso to the output of the shift register - intf.miso <= flop(~intf.sclk, shiftReg.dataOut); + busOut <= shiftReg.stages.rswizzle(); + + intf.miso <= + flop(~intf.sclk, shiftReg.dataOut, + en: ~intf.csb, + reset: reset, + asyncReset: true, + resetValue: busIn?[-1]); } } diff --git a/lib/src/interfaces/spi.dart b/lib/src/interfaces/spi.dart index 3d1f0bcaa..2ec587255 100644 --- a/lib/src/interfaces/spi.dart +++ b/lib/src/interfaces/spi.dart @@ -11,28 +11,29 @@ import 'package:rohd/rohd.dart'; /// A standard SPI interface. class SpiInterface extends PairInterface { - // The width of the data ports [mosi] and [miso]. - // final int dataWidth; - - /// The width of the chip select port [cs]. - // CS as individual lines or one - // final int csWidth; - - // + /// Data length. final int dataLength; - +// TODO(rtorres): add CPOL/CPHA + /// Serial clock. /// + /// Clock signal driven by main. Logic get sclk => port('SCLK'); + /// Main Out Sub In. /// + /// Serial data from main to sub. Logic get mosi => port('MOSI'); + /// Main In Sub Out. /// + /// Serial data from sub to main. Logic get miso => port('MISO'); + /// Chip select (active low). /// - Logic get cs => port('CSB'); //CS bar - + /// Chip select signal from main to sub. + Logic get csb => port('CSB'); + // TODO(cs): add multiple CSB support /// SpiInterface({this.dataLength = 1}) : super( @@ -43,8 +44,4 @@ class SpiInterface extends PairInterface { SpiInterface.clone(SpiInterface super.otherInterface) : dataLength = otherInterface.dataLength, super.clone(); - - // multiple CS or 4 bits in parallel } - -// place for spi mode = cpol and cpha diff --git a/lib/src/models/spi_bfm/spi_main_driver.dart b/lib/src/models/spi_bfm/spi_main_driver.dart index ca39e34da..75659331d 100644 --- a/lib/src/models/spi_bfm/spi_main_driver.dart +++ b/lib/src/models/spi_bfm/spi_main_driver.dart @@ -28,15 +28,28 @@ class SpiMainDriver extends PendingClockedDriver { required super.sequencer, super.dropDelayCycles = 30, String name = 'spiMainDriver', - }) : super(name, parent); + }) : super(name, parent) { + intf.sclk <= ~clk & clkenable; //causing already connected to sclk issue + clkenable.inject(0); + } @override Future run(Phase phase) async { unawaited(super.run(phase)); + // intf.sclk.inject(0); + + intf.sclk.changed.listen((_) { + logger.info('SCLK changed$_'); + }); + + intf.csb.changed.listen((_) { + logger.info('CS changed$_'); + }); + Simulator.injectAction(() { - intf.cs.put(1); - intf.sclk.put(0); + intf.csb.put(1); + // intf.sclk.put(0); intf.mosi.put(0); }); @@ -44,32 +57,36 @@ class SpiMainDriver extends PendingClockedDriver { if (pendingSeqItems.isNotEmpty) { await _drivePacket(pendingSeqItems.removeFirst()); } else { - await clk.nextPosedge; + await clk.nextNegedge; Simulator.injectAction(() { - intf.cs.put(1); - intf.sclk.put(0); + intf.csb.put(1); + clkenable.inject(0); + // intf.sclk.put(0); intf.mosi.put(0); }); } } } + /// + Logic clkenable = Logic(name: 'clkenable'); + /// Drives a packet onto the interface. Future _drivePacket(SpiPacket packet) async { - intf.cs.inject(0); - - // will be extended to multiple CS + intf.csb.inject(0); // Loop through the bits of the packet - for (var i = 0; i < packet.data.width; i++) { + for (var i = 1; i <= packet.data.width; i++) { logger.info('Driving main packet, index: $i'); - intf.mosi.inject(packet.data[i]); - await clk.nextPosedge; - intf.sclk.inject(1); + intf.mosi.inject(packet.data[-i]); + await clk.nextNegedge; + clkenable.inject(1); + // intf.sclk.inject(1); // Wait for the next clock cycle - await clk.nextNegedge; - intf.sclk.inject(0); + await clk.nextPosedge; + + // intf.sclk.inject(0); } } } diff --git a/lib/src/models/spi_bfm/spi_sub_driver.dart b/lib/src/models/spi_bfm/spi_sub_driver.dart index 93830a880..c271ef00d 100644 --- a/lib/src/models/spi_bfm/spi_sub_driver.dart +++ b/lib/src/models/spi_bfm/spi_sub_driver.dart @@ -67,7 +67,7 @@ class SpiSubDriver extends PendingDriver { } } - intf.cs.negedge.listen((_) { + intf.csb.negedge.listen((_) { logger.info('cs negedge'); packetHandler(loadOnly: true); }); diff --git a/pubspec.yaml b/pubspec.yaml index c7241e144..fe4dd760f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,3 +18,9 @@ dependencies: dev_dependencies: logging: ^1.0.1 test: ^1.25.0 + +dependency_overrides: + rohd: + git: + url: https://github.com/intel/rohd + ref: main \ No newline at end of file diff --git a/test/spi/spi_bfm_test.dart b/test/spi/spi_bfm_test.dart index 4f07376c8..77252ce47 100644 --- a/test/spi/spi_bfm_test.dart +++ b/test/spi/spi_bfm_test.dart @@ -18,13 +18,21 @@ import 'package:test/test.dart'; import 'spi_test.dart'; +class SpiMod extends Module { + SpiMod(SpiInterface intf, {super.name = 'SpiModIntf'}) { + intf = SpiInterface.clone(intf) + ..connectIO(this, intf, + inputTags: [PairDirection.fromProvider, PairDirection.fromConsumer]); + } +} + class SpiBfmTest extends Test { late final SpiInterface intf; late final SpiMainAgent main; late final SpiSubAgent sub; late final SpiMonitor monitor; - String get outFolder => 'tmp_test/spibfm/$name/'; + String get outFolder => 'tmp_test/spiBfm/$name/'; SpiBfmTest(super.name) : super() { intf = SpiInterface(dataLength: 8); @@ -95,12 +103,12 @@ void main() { }); Future runTest(SpiBfmTest spiBfmTest, {bool dumpWaves = true}) async { - Simulator.setMaxSimTime(6000); + Simulator.setMaxSimTime(3000); if (dumpWaves) { - final mod = SpiMainIntf(spiBfmTest.intf); + final mod = SpiMod(spiBfmTest.intf); await mod.build(); - WaveDumper(mod); + WaveDumper(mod, outputPath: '${spiBfmTest.outFolder}/waves.vcd'); } await spiBfmTest.start(); diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index de72bc33b..762508b9f 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -10,7 +10,9 @@ import 'dart:async'; // import 'dart:convert'; import 'dart:io'; +import 'dart:math'; +import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; @@ -24,6 +26,7 @@ class SpiMainTest extends Test { late final Logic reset; late final Logic starts; late final Logic clk; + late final Logic busInMain; String get outFolder => 'tmp_test/spiMain/$name/'; @@ -44,11 +47,12 @@ class SpiMainTest extends Test { clk = SimpleClockGenerator(10).clk; // initialize the bus with 00 - final busData = Logic(width: 8)..inject(0x00); + busInMain = Logic(width: 8)..inject(0x00); reset = Logic(); starts = Logic(); - main = SpiMain(busData, intf, clk: clk, reset: reset, start: starts); + main = + SpiMain(intf, busIn: busInMain, clk: clk, reset: reset, start: starts); Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); @@ -74,11 +78,17 @@ class SpiMainTest extends Test { final obj = phase.raiseObjection('SpiMainTestObj'); // reset flow + await clk.waitCycles(1); reset.inject(true); starts.inject(false); await clk.waitCycles(1); reset.inject(false); - //await clk.waitCycles(2); + await clk.waitCycles(1); + reset.inject(true); + + await clk.waitCycles(1); + reset.inject(false); + await clk.waitCycles(1); await stimulus(this); obj.drop(); @@ -113,6 +123,10 @@ class SpiSubTest extends Test { sub = SpiSub(intf: intf, busIn: busIn, reset: reset); + sub.busOut.changed.listen((_) { + logger.info('BusOut changed: ${sub.busOut.value}'); + }); + Directory(outFolder).createSync(recursive: true); final tracker = @@ -141,7 +155,10 @@ class SpiSubTest extends Test { final obj = phase.raiseObjection('SpiSubTestObj'); // reset flow + reset.inject(false); + await clk.waitCycles(1); reset.inject(true); + busIn.inject(0x00); await clk.waitCycles(1); reset.inject(false); //await clk.waitCycles(1); @@ -193,25 +210,24 @@ class SpiPairTest extends Test { busInMain = Logic(width: 8); starts = Logic(); - main = SpiMain(busInMain, intf, clk: clk, reset: resetMain, start: starts); + main = SpiMain(intf, + busIn: busInMain, clk: clk, reset: resetMain, start: starts); //init sub resetSub = Logic(); busInSub = Logic(width: 8); sub = SpiSub(intf: intf, busIn: busInSub, reset: resetSub); - Simulator.registerEndOfSimulationAction(() async { - await tracker.terminate(); - - // // final jsonStr = - // // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); - // // final jsonContents = json.decode(jsonStr); + // sub.busOut.changed.listen((_) { + // logger.info('BusOut changed: ${sub.busOut.value}'); + // }); - // // // ignore: avoid_dynamic_calls - // // expect(jsonContents['records'].length, 2); + intf.miso.changed.listen((_) { + logger.info('Miso changed: ${intf.miso.value}'); + }); - // //Directory(outFolder).deleteSync(recursive: true); - // }); + Simulator.registerEndOfSimulationAction(() async { + await tracker.terminate(); }); monitor.stream.listen(tracker.record); @@ -223,16 +239,26 @@ class SpiPairTest extends Test { final obj = phase.raiseObjection('SpiPairTestObj'); - // reset flow + // Initialize all inputs to initial state. + // Just for waveform clarity. + await clk.waitCycles(1); + + busInMain.inject(00); + busInSub.inject(00); + + starts.inject(false); + resetMain.inject(false); + resetSub.inject(false); + + await clk.waitCycles(1); resetMain.inject(true); resetSub.inject(true); - starts.inject(false); - // extra cycles for easy waveform visibility - await clk.waitCycles(3); + + await clk.waitCycles(1); resetMain.inject(false); resetSub.inject(false); - busInSub.inject(00); + await clk.waitCycles(1); await stimulus(this); obj.drop(); @@ -257,14 +283,22 @@ void main() { await spiMainTest.start(); } - Future sendSubPacket(SpiMainTest test, LogicValue data) async { - test.sub.sequencer.add(SpiPacket(data: data)); + Future sendMainData(SpiMainTest test, int data) async { + test.busInMain.inject(LogicValue.ofInt(data, test.intf.dataLength)); + test.reset.inject(true); + await test.clk.nextPosedge; + test.reset.inject(false); test.starts.inject(true); await test.clk.waitCycles(1); test.starts.inject(false); await test.clk.waitCycles(7); } + Future sendSubPacket(SpiMainTest test, LogicValue data) async { + test.sub.sequencer.add(SpiPacket(data: data.reversed)); + await sendMainData(test, 0x00); + } + test('simple transfers no gap', () async { await runMainTest(SpiMainTest((test) async { await sendSubPacket(test, LogicValue.ofInt(0x72, 8)); @@ -308,7 +342,7 @@ void main() { group('sub gasket tests', () { Future runSubTest(SpiSubTest spiSubTest, {bool dumpWaves = true}) async { - Simulator.setMaxSimTime(6000); + Simulator.setMaxSimTime(3000); final mod = SpiTop(spiSubTest.intf, spiSubTest); if (dumpWaves) { await mod.build(); @@ -318,79 +352,288 @@ void main() { await spiSubTest.start(); } - test('sub tx with busIn and reset', () async { + test('sub tx busOut correctly no gaps', () async { await runSubTest(SpiSubTest((test) async { test.main.sequencer .add(SpiPacket(data: LogicValue.ofInt(0xCD, 8))); // 1100 1101 test.main.sequencer - .add(SpiPacket(data: LogicValue.ofInt(0x2E, 8))); // 0010 1110 - await test.intf.sclk.waitCycles(16); - await test.clk.waitCycles(1); - test.busIn.inject(0x19); // 0001 1001 need to change to LSB. - test.reset.inject(true); - await test.clk.nextChanged; - test.reset.inject(false); + .add(SpiPacket(data: LogicValue.ofInt(0x83, 8))); // 1000 0011 + test.main.sequencer + .add(SpiPacket(data: LogicValue.ofInt(0xE2, 8))); // 1110 0010 + test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x00, 8))); - await test.clk.waitCycles(1); + await test.clk.waitCycles(7); + + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0xCD); + + await test.clk.waitCycles(7); + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0x83); + + await test.clk.waitCycles(7); + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0xE2); + + await test.clk.waitCycles(7); + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0x00); + }, 'testSubA')); + }); + + test('sub tx busOut correctly with gaps', () async { + await runSubTest(SpiSubTest((test) async { + test.main.sequencer + .add(SpiPacket(data: LogicValue.ofInt(0xCD, 8))); // 1100 1101 + + await test.clk.waitCycles(9); + + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0xCD); + + //gap + await test.clk.waitCycles(7); + + test.main.sequencer + .add(SpiPacket(data: LogicValue.ofInt(0x72, 8))); // 0111 0010 + await test.intf.sclk.waitCycles(8); + + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0x72); + + // gap + await test.clk.waitCycles(2); + + test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xAC, 8))); + await test.intf.sclk.waitCycles(8); + + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0xAC); + + // gap + await test.clk.waitCycles(3); + + test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xE2, 8))); test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x00, 8))); - await test.clk.waitCycles(10); + await test.intf.sclk.waitCycles(8); + + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0xE2); + + // waiting for the read packet + await test.clk.waitCycles(7); + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0x00); }, 'testSubA')); }); + + test('sub tx with no gaps, busIn and reset injects', () async { + await runSubTest(SpiSubTest((test) async { + test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xCD, 8))); + test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x72, 8))); + + await test.intf.sclk.waitCycles(7); + + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0xCD); + + await test.clk.waitCycles(7); + + // read busOut here + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0x72); + + // inject bus in on neg edge of same cycle + // will result in 1 clk cycle delay? assuming you had to read on pos and + // inject on neg edge + await test.clk.nextNegedge; + + // td: when busin injected its sent out MSB, should be LSB + test.busIn.inject(0x19); + + //td: but here running into the extra clk cycle issue in main driver? + test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x00, 8))); + + // trigger reset + await test.clk.nextPosedge; + test.reset.inject(true); + await test.clk.waitCycles(1); + test.reset.inject(false); + + await test.clk.waitCycles(7); + + await test.clk.nextPosedge; + expect(test.sub.busOut.value.toInt(), 0x00); + + await test.clk.waitCycles(4); + }, 'testSubC')); + }); }); group('pair of gaskets tests', () { Future runPairTest(SpiPairTest spiPairTest, {bool dumpWaves = true}) async { - Simulator.setMaxSimTime(6000); + Simulator.setMaxSimTime(3000); final mod = SpiTop(spiPairTest.intf, null); if (dumpWaves) { await mod.build(); WaveDumper(mod, outputPath: '${spiPairTest.outFolder}/waves.vcd'); + // print(mod.generateSynth()); } await spiPairTest.start(); } - Future sendMainPacket(SpiPairTest test, LogicValue data) async { - test.busInMain.inject(data); + Future sendMainData(SpiPairTest test, int data) async { + test.busInMain.inject(LogicValue.ofInt(data, test.intf.dataLength)); + await test.clk.nextNegedge; + test.resetMain.inject(true); + await test.clk.nextPosedge; + test.resetMain.inject(false); test.starts.inject(true); await test.clk.waitCycles(1); test.starts.inject(false); await test.clk.waitCycles(7); } - Future sendSubPacket(SpiPairTest test, LogicValue data) async { - test.busInSub.inject(data); + Future sendBothData(SpiPairTest test, + {required int mainData, required int subData}) async { + test.busInSub.inject(LogicValue.ofInt(subData, test.intf.dataLength)); + test.busInMain.inject(LogicValue.ofInt(mainData, test.intf.dataLength)); + await test.clk.nextNegedge; + test.resetSub.inject(true); + test.resetMain.inject(true); + await test.clk.nextPosedge; + test.resetSub.inject(false); + test.resetMain.inject(false); test.starts.inject(true); await test.clk.waitCycles(1); test.starts.inject(false); await test.clk.waitCycles(7); } - test('simple transfers no gap', () async { + void checkMainBusOut(SpiPairTest test, int data) { + if (test.main.busOut.value.toInt() != data) { + test.logger.severe('main busOut: ${test.main.busOut.value}'); + } + } + + void checkSubBusOut(SpiPairTest test, int data) { + if (test.sub.busOut.value.toInt() != data) { + test.logger.severe('sub busOut: ${test.sub.busOut.value}'); + } + } + + test('main busIn injects, both busOut checks, no gaps', () async { await runPairTest(SpiPairTest((test) async { - await sendMainPacket(test, LogicValue.ofInt(0x72, 8)); // 0111 0010 - await sendMainPacket(test, LogicValue.ofInt(0x00, 8)); // 0111 0010 - expect(test.sub.busOut.value.toInt(), 0x72); - await test.clk.waitCycles(2); - // await sendMainPacket(test, LogicValue.ofInt(0xCD, 8)); - // expect(test.main.busOut.value.toInt(), 0xCD); - // await sendMainPacket(test, LogicValue.ofInt(0x56, 8)); - // expect(test.main.busOut.value.toInt(), 0x56); + // Send main data. + await sendMainData(test, 0x73); // 0111 0011 + // Check both busOuts on posEdge of 8th sclk/ negEdge of 8th CLK + checkSubBusOut(test, 0x73); + checkMainBusOut(test, 0x00); + + // Send new main data. + await sendMainData(test, 0xCD); // 1100 1101 + + // check both busOuts, main should equal previous main busIn data + checkSubBusOut(test, 0xCD); + checkMainBusOut(test, 0x73); + + // Send new, check both busOuts + await sendMainData(test, 0xE2); // 1110 0010 + checkSubBusOut(test, 0xE2); + checkMainBusOut(test, 0xCD); + + // Send new, check both busOuts + await sendMainData(test, 0xB3); // 1011 0011 + checkSubBusOut(test, 0xB3); + checkMainBusOut(test, 0xE2); + + // Send new, check both busOuts + await sendMainData(test, 0x00); // 1011 0011 + checkSubBusOut(test, 0x00); + checkMainBusOut(test, 0xB3); }, 'testPairA')); }); - // test('simple transfers with gaps', () async { - // await runBothTest(SpiBothTest((test) async { - // await sendMainPacket(test, LogicValue.ofInt(0x72, 8)); - // await test.clk.waitCycles(1); - // expect(test.main.busOut.value.toInt(), 0x72); - // await sendMainPacket(test, LogicValue.ofInt(0xCD, 8)); - // await test.clk.waitCycles(4); - // expect(test.main.busOut.value.toInt(), 0xCD); - // await sendMainPacket(test, LogicValue.ofInt(0x56, 8)); - // await test.clk.waitCycles(1); - // expect(test.main.busOut.value.toInt(), 0x56); - // }, 'testPairB')); - // }); + test('main busIn injects, both busOut checks, with gaps', () async { + await runPairTest(SpiPairTest((test) async { + // Send main data. + await sendMainData(test, 0x73); // 0111 0011 + // Check both busOuts on posEdge of 8th sclk/ negEdge of 8th CLK + checkSubBusOut(test, 0x73); + checkMainBusOut(test, 0x00); + + // 1 cycle gap + await test.clk.waitCycles(1); + + // Send new main data. + await sendMainData(test, 0xCD); // 1100 1101 + + // check both busOuts, main should equal previous main busIn data + checkSubBusOut(test, 0xCD); + checkMainBusOut(test, 0x73); + await test.clk.waitCycles(1); + // Send new, check both busOuts + await sendMainData(test, 0xE2); // 1110 0010 + checkSubBusOut(test, 0xE2); + checkMainBusOut(test, 0xCD); + await test.clk.waitCycles(3); + // Send new, check both busOuts + await sendMainData(test, 0xB3); // 1011 0011 + checkSubBusOut(test, 0xB3); + checkMainBusOut(test, 0xE2); + + // with gaps + await test.clk.waitCycles(1); + + await sendMainData(test, 0x15); // 0001 0101 + checkSubBusOut(test, 0x15); + checkMainBusOut(test, 0xB3); + + await test.clk.waitCycles(4); + + await sendMainData(test, 0x2D); // 0010 1101 + checkSubBusOut(test, 0x2D); + checkMainBusOut(test, 0x15); + + await test.clk.waitCycles(6); + await sendMainData(test, 0x00); + checkSubBusOut(test, 0x00); + checkMainBusOut(test, 0x2D); + await test.clk.waitCycles(4); + }, 'testPairA')); + }); + + test('main and sub busIn, both busOut checks', () async { + await runPairTest(SpiPairTest((test) async { + // Send regular main data. + // var mainData = Random().nextInt(256); + // var subData = Random().nextInt(256); + await sendBothData(test, mainData: 0x73, subData: 0x00); // 0111 0011 + checkSubBusOut(test, 0x73); + checkMainBusOut(test, 0x00); + + await test.clk.waitCycles(1); + + // Send sub data with main 00 and check busOuts + await sendBothData(test, mainData: 0x00, subData: 0x6A); // 0110 1010 + checkMainBusOut(test, 0x6A); + checkSubBusOut(test, 0x00); + + await test.clk.waitCycles(2); + + await sendBothData(test, mainData: 0x50, subData: 0x82); // 1000 0010 + checkMainBusOut(test, 0x82); + checkSubBusOut(test, 0x50); + + // await test.clk.waitCycles(4); + + await sendBothData(test, mainData: 0x33, subData: 0x7D); // 1000 0010 + checkMainBusOut(test, 0x7D); + checkSubBusOut(test, 0x33); + + await test.clk.waitCycles(4); + }, 'testPairA')); + }); }); } diff --git a/test/spi/spi_main_test.dart b/test/spi/spi_main_test.dart index 017c3cee0..c03e6213f 100644 --- a/test/spi/spi_main_test.dart +++ b/test/spi/spi_main_test.dart @@ -48,7 +48,7 @@ class SpiMainTest extends Test { reset = Logic(); starts = Logic(); - main = SpiMain(busData, intf, clk: clk, reset: reset, start: starts); + main = SpiMain(intf, busIn: busData, clk: clk, reset: reset, start: starts); Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); diff --git a/test/spi/spi_test.dart b/test/spi/spi_test.dart index 944b2c509..ee6475a9c 100644 --- a/test/spi/spi_test.dart +++ b/test/spi/spi_test.dart @@ -12,21 +12,21 @@ import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:test/test.dart'; class SpiMainIntf extends Module { - SpiMainIntf(SpiInterface intf) { + SpiMainIntf(SpiInterface intf, {super.name = 'SpiMainIntf'}) { intf = SpiInterface.clone(intf) ..pairConnectIO(this, intf, PairRole.provider); } } class SpiSubIntf extends Module { - SpiSubIntf(SpiInterface intf) { + SpiSubIntf(SpiInterface intf, {super.name = 'SpiSubIntf'}) { intf = SpiInterface.clone(intf) ..pairConnectIO(this, intf, PairRole.consumer); } } class SpiTopIntf extends Module { - SpiTopIntf() { + SpiTopIntf({super.name = 'SpiTopIntf'}) { final intf = SpiInterface(); SpiMainIntf(intf); SpiSubIntf(intf); From 4e9e6337a37a11e27f3f25f524545ae0a3af62c4 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Fri, 20 Dec 2024 10:48:41 -0800 Subject: [PATCH 17/31] all basic tests passinggit add . reversed in main to sub bfm needed still --- test/spi/spi_gaskets_test.dart | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index 762508b9f..94509aa60 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -155,10 +155,10 @@ class SpiSubTest extends Test { final obj = phase.raiseObjection('SpiSubTestObj'); // reset flow + busIn.inject(0x00); reset.inject(false); await clk.waitCycles(1); reset.inject(true); - busIn.inject(0x00); await clk.waitCycles(1); reset.inject(false); //await clk.waitCycles(1); @@ -296,6 +296,7 @@ void main() { Future sendSubPacket(SpiMainTest test, LogicValue data) async { test.sub.sequencer.add(SpiPacket(data: data.reversed)); + // TODO: fix reversed on subdriver await sendMainData(test, 0x00); } @@ -362,7 +363,7 @@ void main() { .add(SpiPacket(data: LogicValue.ofInt(0xE2, 8))); // 1110 0010 test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x00, 8))); - await test.clk.waitCycles(7); + await test.clk.waitCycles(8); await test.clk.nextPosedge; expect(test.sub.busOut.value.toInt(), 0xCD); @@ -378,6 +379,7 @@ void main() { await test.clk.waitCycles(7); await test.clk.nextPosedge; expect(test.sub.busOut.value.toInt(), 0x00); + await test.clk.waitCycles(4); }, 'testSubA')); }); @@ -386,7 +388,7 @@ void main() { test.main.sequencer .add(SpiPacket(data: LogicValue.ofInt(0xCD, 8))); // 1100 1101 - await test.clk.waitCycles(9); + await test.clk.waitCycles(8); await test.clk.nextPosedge; expect(test.sub.busOut.value.toInt(), 0xCD); @@ -396,7 +398,7 @@ void main() { test.main.sequencer .add(SpiPacket(data: LogicValue.ofInt(0x72, 8))); // 0111 0010 - await test.intf.sclk.waitCycles(8); + await test.clk.waitCycles(8); await test.clk.nextPosedge; expect(test.sub.busOut.value.toInt(), 0x72); @@ -405,7 +407,7 @@ void main() { await test.clk.waitCycles(2); test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xAC, 8))); - await test.intf.sclk.waitCycles(8); + await test.clk.waitCycles(8); await test.clk.nextPosedge; expect(test.sub.busOut.value.toInt(), 0xAC); @@ -415,7 +417,7 @@ void main() { test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xE2, 8))); test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x00, 8))); - await test.intf.sclk.waitCycles(8); + await test.clk.waitCycles(8); await test.clk.nextPosedge; expect(test.sub.busOut.value.toInt(), 0xE2); @@ -432,7 +434,7 @@ void main() { test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0xCD, 8))); test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x72, 8))); - await test.intf.sclk.waitCycles(7); + await test.clk.waitCycles(8); await test.clk.nextPosedge; expect(test.sub.busOut.value.toInt(), 0xCD); @@ -466,7 +468,7 @@ void main() { expect(test.sub.busOut.value.toInt(), 0x00); await test.clk.waitCycles(4); - }, 'testSubC')); + }, 'testSubA')); }); }); From 0b961c696aa300d28ce7e23d14c130d5b34928b4 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Mon, 30 Dec 2024 23:57:22 -0800 Subject: [PATCH 18/31] draft push --- lib/src/gaskets/spi/spi_main.dart | 30 +++-- lib/src/gaskets/spi/spi_sub.dart | 18 ++- lib/src/interfaces/spi.dart | 32 ++--- lib/src/models/spi_bfm/spi_main_driver.dart | 9 +- lib/src/models/spi_bfm/spi_monitor.dart | 4 +- lib/src/models/spi_bfm/spi_packet.dart | 8 +- lib/src/models/spi_bfm/spi_sub_agent.dart | 1 - test/spi/spi_bfm_test.dart | 19 --- test/spi/spi_gaskets_test.dart | 3 - test/spi/spi_main_test.dart | 138 -------------------- test/spi/spi_sub_test.dart | 91 ------------- 11 files changed, 52 insertions(+), 301 deletions(-) delete mode 100644 test/spi/spi_main_test.dart delete mode 100644 test/spi/spi_sub_test.dart diff --git a/lib/src/gaskets/spi/spi_main.dart b/lib/src/gaskets/spi/spi_main.dart index c7901fd01..0d16ac1dd 100644 --- a/lib/src/gaskets/spi/spi_main.dart +++ b/lib/src/gaskets/spi/spi_main.dart @@ -10,7 +10,7 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -/// Main component for SPI Interface. +/// Main component for Serial Peripheral Interface (SPI). class SpiMain extends Module { /// Output bus from Main. Logic get busOut => output('busOut'); @@ -18,7 +18,16 @@ class SpiMain extends Module { /// Done signal from Main. Logic get done => output('done'); - /// Constructs a SPI Main component. + /// Creates a SPI Main component that interfaces with [SpiInterface]. + /// + /// The SPI Main component will drive a clock signal on [SpiInterface.sclk], + /// chip select on [SpiInterface.csb], shift data out on [SpiInterface.mosi], + /// and shift data in from [SpiInterface.miso]. Data to shift out is provided + /// on [busIn]. Data shifted in from [SpiInterface.miso] will be available on + /// [busOut]. After data is available on [busIn], pulsing [reset] will load + /// the data, and pulsing [start] will begin transmitting data until all bits + /// from [busIn] are shifted out. After transmissions is complete [done] + /// signal will go high. SpiMain(SpiInterface intf, {required Logic clk, required Logic reset, @@ -44,27 +53,25 @@ class SpiMain extends Module { final count = Counter.simple( clk: ~clk, - // enable: start | (isRunning & ~done), enable: start | isRunning, reset: reset, minValue: 1, maxValue: busIn.width); - // done <= flop(~clk, count.equalsMax, reset: reset, asyncReset: true); + // Done signal will be high when the counter is at the max value. done <= count.equalsMax; - // Will run when start is pulsed high, reset on reset or when serializer is - // done and start is low. + + // isRunning will be high when start is pulsed high or counter is not done. isRunning <= flop( clk, - // start | (isRunning & ~done), start | ~done, - // en: start, reset: reset, asyncReset: true, ); // Shift register in from MISO. + // NOTE: Reset values are set to busIn values. final shiftReg = ShiftRegister( intf.miso, clk: intf.sclk, @@ -74,16 +81,17 @@ class SpiMain extends Module { resetValue: busIn.elements, ); - // Each busOut bit is connected to the corresponding shift Register stage + // busOut bits are connected to the corresponding shift register data stage. + // NOTE: dataStage0 corresponds to the last bit shifted in. busOut <= shiftReg.stages.rswizzle(); - // Sclk runs off clk when isRunning is true or start is pulsed high. + // SCLK runs off clk when isRunning is true or start is pulsed high. intf.sclk <= ~clk & (isRunning | start); // CS is active low. It will go low when isRunning or start is pulsed high. intf.csb <= ~(isRunning | start); - // Mosi is connected to the serializer output. + // MOSI is connected shift register dataOut. intf.mosi <= flop(~intf.sclk, shiftReg.dataOut, reset: reset, asyncReset: true, resetValue: busIn[-1]); diff --git a/lib/src/gaskets/spi/spi_sub.dart b/lib/src/gaskets/spi/spi_sub.dart index a8e47ca57..36c015631 100644 --- a/lib/src/gaskets/spi/spi_sub.dart +++ b/lib/src/gaskets/spi/spi_sub.dart @@ -10,12 +10,20 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; -/// Sub component for SPI Interface. +/// Sub component for Serial Peripheral Interface (SPI). class SpiSub extends Module { /// Output bus from Sub. Logic get busOut => output('busOut'); + /// Creates a SPI Sub component that interfaces with [SpiInterface]. /// + /// The SPI Sub component will enable via chip select from [SpiInterface.csb]. + /// Clock signal will be received on [SpiInterface.sclk], data will shift in + /// from [SpiInterface.mosi], and shift data out from [SpiInterface.miso]. + /// Data to shift out is provided from [busIn]. Data shifted in from + /// [SpiInterface.mosi] will be available on [busOut]. After data is available + /// on [busIn], pulsing [reset] will load the data, and a bit of data will be + /// transmitted per clock pulse. SpiSub( {required SpiInterface intf, Logic? busIn, @@ -31,7 +39,6 @@ class SpiSub extends Module { } // Reset signal for sub, if provided. - // will need to be toggled to load new busIn values if (reset != null) { reset = addInput('reset', reset); } @@ -39,7 +46,8 @@ class SpiSub extends Module { // Bus Output from Sub addOutput('busOut', width: intf.dataLength); - // Shift Register + // Shift Register in from MOSI. + // NOTE: Reset values are set to busIn values. final shiftReg = ShiftRegister( intf.mosi, enable: ~intf.csb, @@ -50,9 +58,11 @@ class SpiSub extends Module { resetValue: busIn?.elements, ); - // BusOut is connected to the stages of the shift register + // busOut bits are connected to the corresponding shift register data stage. + // NOTE: dataStage0 corresponds to the last bit shifted in. busOut <= shiftReg.stages.rswizzle(); + // MISO is connected to shift register dataOut. intf.miso <= flop(~intf.sclk, shiftReg.dataOut, en: ~intf.csb, diff --git a/lib/src/interfaces/spi.dart b/lib/src/interfaces/spi.dart index 2ec587255..ac5407d01 100644 --- a/lib/src/interfaces/spi.dart +++ b/lib/src/interfaces/spi.dart @@ -2,45 +2,39 @@ // SPDX-License-Identifier: BSD-3-Clause // // spi.dart -// Definitions for the SPI interface. +// Definitions for Serial Peripheral Interface (SPI). // // 2024 September 23 // Author: Roberto Torres import 'package:rohd/rohd.dart'; -/// A standard SPI interface. +/// A standard Serial Peripheral Interface. class SpiInterface extends PairInterface { - /// Data length. + /// The data length for serial transmissions on this interface. final int dataLength; -// TODO(rtorres): add CPOL/CPHA - /// Serial clock. - /// - /// Clock signal driven by main. + + /// Serial clock (SCLK). Clock signal from main to sub(s). Logic get sclk => port('SCLK'); + // TODO(rt): add CPOL/CPHA support - /// Main Out Sub In. - /// - /// Serial data from main to sub. + /// Main Out Sub In (MOSI). Serial data from main to sub(s). Logic get mosi => port('MOSI'); - /// Main In Sub Out. - /// - /// Serial data from sub to main. + /// Main In Sub Out (MISO). Serial data from sub(s) to main. Logic get miso => port('MISO'); - /// Chip select (active low). - /// - /// Chip select signal from main to sub. + /// Chip select (active low). Chip select signal from main to sub. Logic get csb => port('CSB'); - // TODO(cs): add multiple CSB support - /// + // TODO(rt): add multiple CSB support + + /// Creates a new [SpiInterface]. SpiInterface({this.dataLength = 1}) : super( portsFromConsumer: [Port('MISO')], portsFromProvider: [Port('MOSI'), Port('CSB'), Port('SCLK')]); - /// + /// Clones this [SpiInterface]. SpiInterface.clone(SpiInterface super.otherInterface) : dataLength = otherInterface.dataLength, super.clone(); diff --git a/lib/src/models/spi_bfm/spi_main_driver.dart b/lib/src/models/spi_bfm/spi_main_driver.dart index 75659331d..6f7c3aaa6 100644 --- a/lib/src/models/spi_bfm/spi_main_driver.dart +++ b/lib/src/models/spi_bfm/spi_main_driver.dart @@ -37,8 +37,6 @@ class SpiMainDriver extends PendingClockedDriver { Future run(Phase phase) async { unawaited(super.run(phase)); - // intf.sclk.inject(0); - intf.sclk.changed.listen((_) { logger.info('SCLK changed$_'); }); @@ -49,7 +47,6 @@ class SpiMainDriver extends PendingClockedDriver { Simulator.injectAction(() { intf.csb.put(1); - // intf.sclk.put(0); intf.mosi.put(0); }); @@ -61,14 +58,13 @@ class SpiMainDriver extends PendingClockedDriver { Simulator.injectAction(() { intf.csb.put(1); clkenable.inject(0); - // intf.sclk.put(0); intf.mosi.put(0); }); } } } - /// + /// Clock enable signal. Logic clkenable = Logic(name: 'clkenable'); /// Drives a packet onto the interface. @@ -81,12 +77,9 @@ class SpiMainDriver extends PendingClockedDriver { intf.mosi.inject(packet.data[-i]); await clk.nextNegedge; clkenable.inject(1); - // intf.sclk.inject(1); // Wait for the next clock cycle await clk.nextPosedge; - - // intf.sclk.inject(0); } } } diff --git a/lib/src/models/spi_bfm/spi_monitor.dart b/lib/src/models/spi_bfm/spi_monitor.dart index d7d5d3569..3adddea1c 100644 --- a/lib/src/models/spi_bfm/spi_monitor.dart +++ b/lib/src/models/spi_bfm/spi_monitor.dart @@ -18,7 +18,7 @@ class SpiMonitor extends Monitor { /// The interface to watch. final SpiInterface intf; - /// + /// The direction to monitor. final SpiDirection? direction; /// Creates a new [SpiMonitor] for [intf]. @@ -29,7 +29,7 @@ class SpiMonitor extends Monitor { String name = 'spiMonitor'}) : super(name, parent); - /// + /// Run function. @override Future run(Phase phase) async { unawaited(super.run(phase)); diff --git a/lib/src/models/spi_bfm/spi_packet.dart b/lib/src/models/spi_bfm/spi_packet.dart index 9032fc011..00750fccd 100644 --- a/lib/src/models/spi_bfm/spi_packet.dart +++ b/lib/src/models/spi_bfm/spi_packet.dart @@ -13,16 +13,15 @@ import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; -/// Doc [main] f -/// Doc [sub] f +/// Direction of the packet. enum SpiDirection { main, sub } /// A packet for the [SpiInterface]. class SpiPacket extends SequenceItem implements Trackable { - /// + /// The data in the packet. final LogicValue data; - /// + /// Direction of the packet. final SpiDirection? direction; /// Creates a new packet. @@ -37,7 +36,6 @@ class SpiPacket extends SequenceItem implements Trackable { _completer.complete(); } - /// @override String? trackerString(TrackerField field) { switch (field.title) { diff --git a/lib/src/models/spi_bfm/spi_sub_agent.dart b/lib/src/models/spi_bfm/spi_sub_agent.dart index b0fbb825e..a1c347181 100644 --- a/lib/src/models/spi_bfm/spi_sub_agent.dart +++ b/lib/src/models/spi_bfm/spi_sub_agent.dart @@ -38,7 +38,6 @@ class SpiSubAgent extends Agent { sequencer: sequencer, ); - /// monitor = SpiMonitor( parent: this, direction: SpiDirection.main, diff --git a/test/spi/spi_bfm_test.dart b/test/spi/spi_bfm_test.dart index 77252ce47..0b01884bf 100644 --- a/test/spi/spi_bfm_test.dart +++ b/test/spi/spi_bfm_test.dart @@ -8,7 +8,6 @@ // Author: Roberto Torres import 'dart:async'; -// import 'dart:convert'; import 'dart:io'; import 'package:rohd/rohd.dart'; @@ -16,8 +15,6 @@ import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; import 'package:test/test.dart'; -import 'spi_test.dart'; - class SpiMod extends Module { SpiMod(SpiInterface intf, {super.name = 'SpiModIntf'}) { intf = SpiInterface.clone(intf) @@ -52,16 +49,6 @@ class SpiBfmTest extends Test { Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); - - // // Commented to avoid bug - // final jsonStr = - // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); - // final jsonContents = json.decode(jsonStr); - - // // ignore: avoid_dynamic_calls - // expect(jsonContents['records'].length, 2); - - // Directory(outFolder).deleteSync(recursive: true); }); monitor.stream.listen(tracker.record); @@ -78,8 +65,6 @@ class SpiBfmTest extends Test { main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x00, 8))); - //main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x00, 8))); - unawaited(monitor.stream .where((event) => event.direction == SpiDirection.main && event.data.toInt() == 0xCB) @@ -89,10 +74,6 @@ class SpiBfmTest extends Test { .add(SpiPacket(data: LogicValue.ofInt(0x1B, 8))); //0b0001 1011 = 27 })); - // main.sequencer.add(SpiPacket( - // data: LogicValue.ofInt(0x00, 8), - // direction: SpiDirection.read)); //0b0111 0001 = 113 - obj.drop(); } } diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index 94509aa60..5a136ddd2 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -8,11 +8,8 @@ // Author: Roberto Torres import 'dart:async'; -// import 'dart:convert'; import 'dart:io'; -import 'dart:math'; -import 'package:collection/collection.dart'; import 'package:rohd/rohd.dart'; import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; diff --git a/test/spi/spi_main_test.dart b/test/spi/spi_main_test.dart deleted file mode 100644 index c03e6213f..000000000 --- a/test/spi/spi_main_test.dart +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: BSD-3-Clause -// -// spi_main_test.dart -// Tests for SPI main gasket. -// -// 2024 October 10 -// Author: Roberto Torres - -import 'dart:async'; -// import 'dart:convert'; -import 'dart:io'; - -import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_vf/rohd_vf.dart'; -import 'package:test/test.dart'; - -class SpiMainTest extends Test { - late final SpiInterface intf; - late final SpiSubAgent sub; - late final SpiMonitor monitor; - late final SpiMain main; - late final Logic reset; - late final Logic starts; - late final Logic clk; - - String get outFolder => 'tmp_test/spiMain/$name/'; - - final Future Function(SpiMainTest test) stimulus; - - SpiMainTest(this.stimulus, super.name) : super() { - intf = SpiInterface(dataLength: 8); - - sub = SpiSubAgent(intf: intf, parent: this); - - monitor = SpiMonitor(intf: intf, parent: this); - - Directory(outFolder).createSync(recursive: true); - - final tracker = - SpiTracker(intf: intf, dumpTable: true, outputFolder: outFolder); - - clk = SimpleClockGenerator(10).clk; - - // initialize the bus with 00 - final busData = Logic(width: 8)..inject(0x00); - reset = Logic(); - starts = Logic(); - - main = SpiMain(intf, busIn: busData, clk: clk, reset: reset, start: starts); - - Simulator.registerEndOfSimulationAction(() async { - await tracker.terminate(); - - // // final jsonStr = - // // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); - // // final jsonContents = json.decode(jsonStr); - - // // // ignore: avoid_dynamic_calls - // // expect(jsonContents['records'].length, 2); - - // //Directory(outFolder).deleteSync(recursive: true); - // }); - }); - - monitor.stream.listen(tracker.record); - } - - @override - Future run(Phase phase) async { - unawaited(super.run(phase)); - - final obj = phase.raiseObjection('SpiMainTestObj'); - - // reset flow - reset.inject(true); - starts.inject(false); - await clk.waitCycles(2); - reset.inject(false); - - await stimulus(this); - - obj.drop(); - } -} - -void main() { - tearDown(() async { - await Simulator.reset(); - }); - - group('main gasket tests', () { - Future runMainTest(SpiMainTest spiMainTest, - {bool dumpWaves = true}) async { - Simulator.setMaxSimTime(6000); - - if (dumpWaves) { - await spiMainTest.main.build(); - WaveDumper(spiMainTest.main); - } - await spiMainTest.start(); - } - - Future sendPacket(SpiMainTest test, LogicValue data) async { - test.sub.sequencer.add(SpiPacket(data: data)); - test.starts.inject(true); - await test.clk.waitCycles(1); - test.starts.inject(false); - await test.clk.waitCycles(7); - } - - test('simple transfers no gap', () async { - await runMainTest(SpiMainTest((test) async { - await sendPacket(test, LogicValue.ofInt(0x72, 8)); - expect(test.main.busOut.value.toInt(), 0x72); - await sendPacket(test, LogicValue.ofInt(0xCD, 8)); - expect(test.main.busOut.value.toInt(), 0xCD); - await sendPacket(test, LogicValue.ofInt(0x56, 8)); - expect(test.main.busOut.value.toInt(), 0x56); - }, 'testMainA')); - }); - - test('simple transfers with gaps', () async { - await runMainTest(SpiMainTest((test) async { - await sendPacket(test, LogicValue.ofInt(0x72, 8)); - await test.clk.waitCycles(1); - expect(test.main.busOut.value.toInt(), 0x72); - await sendPacket(test, LogicValue.ofInt(0xCD, 8)); - await test.clk.waitCycles(4); - expect(test.main.busOut.value.toInt(), 0xCD); - await sendPacket(test, LogicValue.ofInt(0x56, 8)); - await test.clk.waitCycles(1); - expect(test.main.busOut.value.toInt(), 0x56); - }, 'testMainB')); - }); - }); -} diff --git a/test/spi/spi_sub_test.dart b/test/spi/spi_sub_test.dart deleted file mode 100644 index 272ef4eea..000000000 --- a/test/spi/spi_sub_test.dart +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (C) 2024 Intel Corporation -// SPDX-License-Identifier: BSD-3-Clause -// -// spi_sub_test.dart -// Tests for SPI Sub gasket. -// -// 2024 October 10 -// Author: Roberto Torres - -import 'dart:async'; -// import 'dart:convert'; -import 'dart:io'; - -import 'package:rohd/rohd.dart'; -import 'package:rohd_hcl/rohd_hcl.dart'; -import 'package:rohd_vf/rohd_vf.dart'; -import 'package:test/test.dart'; - -class SpiSubTest extends Test { - late final SpiInterface intf; - late final SpiMainAgent main; - late final SpiMonitor monitor; - - String get outFolder => 'tmp_test/spibfm/$name/'; - - SpiSubTest(super.name) : super() { - intf = SpiInterface(dataLength: 8); - - final clk = SimpleClockGenerator(10).clk; - - main = SpiMainAgent(intf: intf, parent: this, clk: clk); - - monitor = SpiMonitor(intf: intf, parent: this); - - Directory(outFolder).createSync(recursive: true); - - final tracker = - SpiTracker(intf: intf, dumpTable: true, outputFolder: outFolder); - - Simulator.registerEndOfSimulationAction(() async { - await tracker.terminate(); - - // final jsonStr = - // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); - // final jsonContents = json.decode(jsonStr); - - // // ignore: avoid_dynamic_calls - // expect(jsonContents['records'].length, 2); - - //Directory(outFolder).deleteSync(recursive: true); - }); - - monitor.stream.listen(tracker.record); - } - - @override - Future run(Phase phase) async { - unawaited(super.run(phase)); - - final obj = phase.raiseObjection('SpiSubTestObj'); - - main.sequencer - .add(SpiPacket(data: LogicValue.ofInt(0xCB, 8))); //0b1100 1011 = 203 - - main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x22, 8))); - - obj.drop(); - } -} - -void main() { - tearDown(() async { - await Simulator.reset(); - }); - - Future runTest(SpiSubTest spiSubTest, {bool dumpWaves = true}) async { - Simulator.setMaxSimTime(6000); - final sub = SpiSub(intf: spiSubTest.intf); - - if (dumpWaves) { - await sub.build(); - WaveDumper(sub); - } - - await spiSubTest.start(); - } - - test('simple transfers', () async { - await runTest(SpiSubTest('simple')); - }); -} From a15faf099b40b2dc5ea4c1285646e232652c20d7 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Fri, 10 Jan 2025 09:26:12 -0800 Subject: [PATCH 19/31] consolidate onto spi.dart --- lib/rohd_hcl.dart | 3 +-- lib/src/gaskets/spi/spi.dart | 5 +++++ lib/src/models/spi_bfm/spi_main_driver.dart | 10 +--------- lib/src/models/spi_bfm/spi_monitor.dart | 2 +- lib/src/models/spi_bfm/spi_sub_driver.dart | 5 ----- 5 files changed, 8 insertions(+), 17 deletions(-) create mode 100644 lib/src/gaskets/spi/spi.dart diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index 7a599cd1d..6b2cbcf0a 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -13,8 +13,7 @@ export 'src/exceptions.dart'; export 'src/extrema.dart'; export 'src/fifo.dart'; export 'src/find.dart'; -export 'src/gaskets/spi/spi_main.dart'; -export 'src/gaskets/spi/spi_sub.dart'; +export 'src/gaskets/spi/spi.dart'; export 'src/interfaces/interfaces.dart'; export 'src/memory/memories.dart'; export 'src/models/models.dart'; diff --git a/lib/src/gaskets/spi/spi.dart b/lib/src/gaskets/spi/spi.dart new file mode 100644 index 000000000..8660514dc --- /dev/null +++ b/lib/src/gaskets/spi/spi.dart @@ -0,0 +1,5 @@ +// Copyright (C) 2024-2025 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause + +export 'spi_main.dart'; +export 'spi_sub.dart'; diff --git a/lib/src/models/spi_bfm/spi_main_driver.dart b/lib/src/models/spi_bfm/spi_main_driver.dart index 6f7c3aaa6..a4fdfb9ca 100644 --- a/lib/src/models/spi_bfm/spi_main_driver.dart +++ b/lib/src/models/spi_bfm/spi_main_driver.dart @@ -29,7 +29,7 @@ class SpiMainDriver extends PendingClockedDriver { super.dropDelayCycles = 30, String name = 'spiMainDriver', }) : super(name, parent) { - intf.sclk <= ~clk & clkenable; //causing already connected to sclk issue + intf.sclk <= ~clk & clkenable; clkenable.inject(0); } @@ -37,14 +37,6 @@ class SpiMainDriver extends PendingClockedDriver { Future run(Phase phase) async { unawaited(super.run(phase)); - intf.sclk.changed.listen((_) { - logger.info('SCLK changed$_'); - }); - - intf.csb.changed.listen((_) { - logger.info('CS changed$_'); - }); - Simulator.injectAction(() { intf.csb.put(1); intf.mosi.put(0); diff --git a/lib/src/models/spi_bfm/spi_monitor.dart b/lib/src/models/spi_bfm/spi_monitor.dart index 3adddea1c..13cd775fe 100644 --- a/lib/src/models/spi_bfm/spi_monitor.dart +++ b/lib/src/models/spi_bfm/spi_monitor.dart @@ -18,7 +18,7 @@ class SpiMonitor extends Monitor { /// The interface to watch. final SpiInterface intf; - /// The direction to monitor. + /// The direction to monitor. If null, monitors both directions. final SpiDirection? direction; /// Creates a new [SpiMonitor] for [intf]. diff --git a/lib/src/models/spi_bfm/spi_sub_driver.dart b/lib/src/models/spi_bfm/spi_sub_driver.dart index c271ef00d..3d62782f3 100644 --- a/lib/src/models/spi_bfm/spi_sub_driver.dart +++ b/lib/src/models/spi_bfm/spi_sub_driver.dart @@ -47,12 +47,9 @@ class SpiSubDriver extends PendingDriver { if (packet != null) { if (loadOnly) { intf.miso.inject(packet!.data[dataIndex!]); - logger.info('injected sub packet, index: $dataIndex'); } else { dataIndex = dataIndex! + 1; - logger.info('incremented index to: $dataIndex'); if (dataIndex! < packet!.data.width) { - logger.info('injecting sub packet, index: $dataIndex'); intf.miso.inject(packet!.data[dataIndex!]); } } @@ -68,12 +65,10 @@ class SpiSubDriver extends PendingDriver { } intf.csb.negedge.listen((_) { - logger.info('cs negedge'); packetHandler(loadOnly: true); }); intf.sclk.negedge.listen((_) { - logger.info('sclk negedge'); packetHandler(loadOnly: false); }); } From 979d933c711cd25f951d8b1e19ecc915c407883b Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Tue, 21 Jan 2025 12:42:03 -0800 Subject: [PATCH 20/31] cleanup --- lib/rohd_hcl.dart | 2 +- lib/src/gaskets/spi/spi_main.dart | 2 +- lib/src/gaskets/spi/spi_sub.dart | 2 +- lib/src/interfaces/spi.dart | 2 +- lib/src/models/spi_bfm/spi_main_driver.dart | 2 +- lib/src/models/spi_bfm/spi_sub_driver.dart | 67 +++++++++++---------- lib/src/shift_register.dart | 8 ++- test/spi/spi_gaskets_test.dart | 7 +-- 8 files changed, 45 insertions(+), 47 deletions(-) diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index 6b2cbcf0a..331931e12 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2023-2024 Intel Corporation +// Copyright (C) 2023-2025 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause export 'src/arbiters/arbiters.dart'; diff --git a/lib/src/gaskets/spi/spi_main.dart b/lib/src/gaskets/spi/spi_main.dart index 0d16ac1dd..5878ff2a8 100644 --- a/lib/src/gaskets/spi/spi_main.dart +++ b/lib/src/gaskets/spi/spi_main.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // spi_main.dart diff --git a/lib/src/gaskets/spi/spi_sub.dart b/lib/src/gaskets/spi/spi_sub.dart index 36c015631..2e288e4a7 100644 --- a/lib/src/gaskets/spi/spi_sub.dart +++ b/lib/src/gaskets/spi/spi_sub.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // spi_sub.dart diff --git a/lib/src/interfaces/spi.dart b/lib/src/interfaces/spi.dart index ac5407d01..cf19c25a2 100644 --- a/lib/src/interfaces/spi.dart +++ b/lib/src/interfaces/spi.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // spi.dart diff --git a/lib/src/models/spi_bfm/spi_main_driver.dart b/lib/src/models/spi_bfm/spi_main_driver.dart index a4fdfb9ca..363af2735 100644 --- a/lib/src/models/spi_bfm/spi_main_driver.dart +++ b/lib/src/models/spi_bfm/spi_main_driver.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // spi_main_driver.dart diff --git a/lib/src/models/spi_bfm/spi_sub_driver.dart b/lib/src/models/spi_bfm/spi_sub_driver.dart index 3d62782f3..76d114ba3 100644 --- a/lib/src/models/spi_bfm/spi_sub_driver.dart +++ b/lib/src/models/spi_bfm/spi_sub_driver.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // spi_sub_driver.dart @@ -30,46 +30,47 @@ class SpiSubDriver extends PendingDriver { unawaited(super.run(phase)); intf.miso.inject(0); + + intf.csb.negedge.listen((_) { + _packetHandler(loadOnly: true); + }); + + intf.sclk.negedge.listen((_) { + _packetHandler(loadOnly: false); + }); + } + + // Function handles the packet. + void _packetHandler({required bool loadOnly}) { SpiPacket? packet; int? dataIndex; - // Function handles the packet. - void packetHandler({required bool loadOnly}) { - if (packet == null && pendingSeqItems.isNotEmpty) { - packet = pendingSeqItems.removeFirst(); - if (loadOnly) { - dataIndex = 0; - } else { - dataIndex = -1; - } + if (pendingSeqItems.isNotEmpty) { + packet = pendingSeqItems.removeFirst(); + if (loadOnly) { + dataIndex = 0; + } else { + dataIndex = -1; } - if (packet != null) { - if (loadOnly) { - intf.miso.inject(packet!.data[dataIndex!]); - } else { - dataIndex = dataIndex! + 1; - if (dataIndex! < packet!.data.width) { - intf.miso.inject(packet!.data[dataIndex!]); - } + } + if (packet != null) { + if (loadOnly) { + intf.miso.inject(packet.data[dataIndex!]); + } else { + dataIndex = dataIndex! + 1; + if (dataIndex < packet.data.width) { + intf.miso.inject(packet.data[dataIndex]); } + } - if (dataIndex! >= packet!.data.width) { - packet = null; - dataIndex = null; - packetHandler(loadOnly: loadOnly); - } - } else { - intf.miso.inject(0); + if (dataIndex >= packet.data.width) { + packet = null; + dataIndex = null; + _packetHandler(loadOnly: loadOnly); } + } else { + intf.miso.inject(0); } - - intf.csb.negedge.listen((_) { - packetHandler(loadOnly: true); - }); - - intf.sclk.negedge.listen((_) { - packetHandler(loadOnly: false); - }); } } diff --git a/lib/src/shift_register.dart b/lib/src/shift_register.dart index acfcad814..7ef2568b1 100644 --- a/lib/src/shift_register.dart +++ b/lib/src/shift_register.dart @@ -38,9 +38,11 @@ class ShiftRegister extends Module { final String dataName; /// Creates a new shift register with specified [depth] which is only active - /// when [enable]d. If [reset] is provided, it will reset synchronously with - /// [clk] or aynchronously if [asyncReset] is true. The [reset] will reset all - /// stages to a default of `0` or to the provided [resetValue]. + /// when [enable]d. + /// + /// If [reset] is provided, it will reset synchronously with [clk] or + /// aynchronously if [asyncReset] is true. The [reset] will reset allstages to + /// a default of `0` or to the provided [resetValue]. /// If [resetValue] is a [List] the stages will reset to the corresponding /// value in the list. ShiftRegister( diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index 5a136ddd2..a8ce85fa1 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // spi_gaskets_test.dart @@ -158,7 +158,6 @@ class SpiSubTest extends Test { reset.inject(true); await clk.waitCycles(1); reset.inject(false); - //await clk.waitCycles(1); await stimulus(this); obj.drop(); @@ -215,10 +214,6 @@ class SpiPairTest extends Test { busInSub = Logic(width: 8); sub = SpiSub(intf: intf, busIn: busInSub, reset: resetSub); - // sub.busOut.changed.listen((_) { - // logger.info('BusOut changed: ${sub.busOut.value}'); - // }); - intf.miso.changed.listen((_) { logger.info('Miso changed: ${intf.miso.value}'); }); From 97e4577ee7d7e32ed5920aed332d259d0e52b6d4 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Wed, 22 Jan 2025 09:40:08 -0800 Subject: [PATCH 21/31] adding done signal --- lib/src/models/spi_bfm/spi_packet.dart | 2 +- pubspec.yaml | 6 ------ test/spi/spi_bfm_test.dart | 2 +- test/spi/spi_gaskets_test.dart | 29 +++----------------------- 4 files changed, 5 insertions(+), 34 deletions(-) diff --git a/lib/src/models/spi_bfm/spi_packet.dart b/lib/src/models/spi_bfm/spi_packet.dart index 00750fccd..7db441c5c 100644 --- a/lib/src/models/spi_bfm/spi_packet.dart +++ b/lib/src/models/spi_bfm/spi_packet.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // spi_packet.dart diff --git a/pubspec.yaml b/pubspec.yaml index fe4dd760f..c7241e144 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,9 +18,3 @@ dependencies: dev_dependencies: logging: ^1.0.1 test: ^1.25.0 - -dependency_overrides: - rohd: - git: - url: https://github.com/intel/rohd - ref: main \ No newline at end of file diff --git a/test/spi/spi_bfm_test.dart b/test/spi/spi_bfm_test.dart index 0b01884bf..7cf6c5aca 100644 --- a/test/spi/spi_bfm_test.dart +++ b/test/spi/spi_bfm_test.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // spi_bfm_test.dart diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index a8ce85fa1..80ef41616 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -39,7 +39,7 @@ class SpiMainTest extends Test { Directory(outFolder).createSync(recursive: true); final tracker = - SpiTracker(intf: intf, dumpTable: true, outputFolder: outFolder); + SpiTracker(intf: intf, dumpTable: false, outputFolder: outFolder); clk = SimpleClockGenerator(10).clk; @@ -53,16 +53,6 @@ class SpiMainTest extends Test { Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); - - // // final jsonStr = - // // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); - // // final jsonContents = json.decode(jsonStr); - - // // // ignore: avoid_dynamic_calls - // // expect(jsonContents['records'].length, 2); - - // //Directory(outFolder).deleteSync(recursive: true); - // }); }); monitor.stream.listen(tracker.record); @@ -127,19 +117,10 @@ class SpiSubTest extends Test { Directory(outFolder).createSync(recursive: true); final tracker = - SpiTracker(intf: intf, dumpTable: true, outputFolder: outFolder); + SpiTracker(intf: intf, dumpTable: false, outputFolder: outFolder); Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); - - // final jsonStr = - // File('$outFolder/spiTracker.tracker.json').readAsStringSync(); - // final jsonContents = json.decode(jsonStr); - - // // ignore: avoid_dynamic_calls - // expect(jsonContents['records'].length, 2); - - //Directory(outFolder).deleteSync(recursive: true); }); monitor.stream.listen(tracker.record); @@ -197,7 +178,7 @@ class SpiPairTest extends Test { Directory(outFolder).createSync(recursive: true); final tracker = - SpiTracker(intf: intf, dumpTable: true, outputFolder: outFolder); + SpiTracker(intf: intf, dumpTable: false, outputFolder: outFolder); clk = SimpleClockGenerator(10).clk; @@ -214,10 +195,6 @@ class SpiPairTest extends Test { busInSub = Logic(width: 8); sub = SpiSub(intf: intf, busIn: busInSub, reset: resetSub); - intf.miso.changed.listen((_) { - logger.info('Miso changed: ${intf.miso.value}'); - }); - Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); }); From a69ab161d44794f763b2bdcc6171bee7a4f66248 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Thu, 23 Jan 2025 09:25:57 -0800 Subject: [PATCH 22/31] spi sub msb first fixed --- lib/src/gaskets/spi/spi_sub.dart | 2 ++ lib/src/models/spi_bfm/spi_main_driver.dart | 1 - lib/src/models/spi_bfm/spi_monitor.dart | 4 +-- lib/src/models/spi_bfm/spi_sub_driver.dart | 32 +++++++++++---------- lib/src/shift_register.dart | 11 +++---- lib/src/summation/counter.dart | 1 + test/spi/spi_bfm_test.dart | 12 ++++++++ test/spi/spi_gaskets_test.dart | 16 ++--------- test/spi/spi_test.dart | 3 +- 9 files changed, 45 insertions(+), 37 deletions(-) diff --git a/lib/src/gaskets/spi/spi_sub.dart b/lib/src/gaskets/spi/spi_sub.dart index 2e288e4a7..5c1bd1627 100644 --- a/lib/src/gaskets/spi/spi_sub.dart +++ b/lib/src/gaskets/spi/spi_sub.dart @@ -46,6 +46,8 @@ class SpiSub extends Module { // Bus Output from Sub addOutput('busOut', width: intf.dataLength); + addOutput('done'); + // Shift Register in from MOSI. // NOTE: Reset values are set to busIn values. final shiftReg = ShiftRegister( diff --git a/lib/src/models/spi_bfm/spi_main_driver.dart b/lib/src/models/spi_bfm/spi_main_driver.dart index 363af2735..65bae935a 100644 --- a/lib/src/models/spi_bfm/spi_main_driver.dart +++ b/lib/src/models/spi_bfm/spi_main_driver.dart @@ -65,7 +65,6 @@ class SpiMainDriver extends PendingClockedDriver { // Loop through the bits of the packet for (var i = 1; i <= packet.data.width; i++) { - logger.info('Driving main packet, index: $i'); intf.mosi.inject(packet.data[-i]); await clk.nextNegedge; clkenable.inject(1); diff --git a/lib/src/models/spi_bfm/spi_monitor.dart b/lib/src/models/spi_bfm/spi_monitor.dart index 13cd775fe..9d35316ca 100644 --- a/lib/src/models/spi_bfm/spi_monitor.dart +++ b/lib/src/models/spi_bfm/spi_monitor.dart @@ -47,12 +47,12 @@ class SpiMonitor extends Monitor { if (dataListWrite.length == intf.dataLength) { add(SpiPacket( - data: dataListWrite.rswizzle(), direction: SpiDirection.main)); + data: dataListWrite.swizzle(), direction: SpiDirection.main)); dataListWrite.clear(); } if (dataListRead.length == intf.dataLength) { add(SpiPacket( - data: dataListRead.rswizzle(), direction: SpiDirection.sub)); + data: dataListRead.swizzle(), direction: SpiDirection.sub)); dataListRead.clear(); } }); diff --git a/lib/src/models/spi_bfm/spi_sub_driver.dart b/lib/src/models/spi_bfm/spi_sub_driver.dart index 76d114ba3..463ee5271 100644 --- a/lib/src/models/spi_bfm/spi_sub_driver.dart +++ b/lib/src/models/spi_bfm/spi_sub_driver.dart @@ -40,33 +40,35 @@ class SpiSubDriver extends PendingDriver { }); } - // Function handles the packet. - void _packetHandler({required bool loadOnly}) { - SpiPacket? packet; + /// The pending packet. + SpiPacket? _packet; - int? dataIndex; + /// + int? _dataIndex; + // Function handles the packet. + void _packetHandler({required bool loadOnly}) { if (pendingSeqItems.isNotEmpty) { - packet = pendingSeqItems.removeFirst(); + _packet = pendingSeqItems.removeFirst(); if (loadOnly) { - dataIndex = 0; + _dataIndex = _packet!.data.width - 1; } else { - dataIndex = -1; + _dataIndex = _packet!.data.width; } } - if (packet != null) { + if (_packet != null) { if (loadOnly) { - intf.miso.inject(packet.data[dataIndex!]); + intf.miso.inject(_packet!.data[_dataIndex!]); } else { - dataIndex = dataIndex! + 1; - if (dataIndex < packet.data.width) { - intf.miso.inject(packet.data[dataIndex]); + _dataIndex = _dataIndex! - 1; + if (_dataIndex! > -1) { + intf.miso.inject(_packet!.data[_dataIndex!]); } } - if (dataIndex >= packet.data.width) { - packet = null; - dataIndex = null; + if (_dataIndex! <= -1) { + _packet = null; + _dataIndex = null; _packetHandler(loadOnly: loadOnly); } } else { diff --git a/lib/src/shift_register.dart b/lib/src/shift_register.dart index acfcad814..30b505a33 100644 --- a/lib/src/shift_register.dart +++ b/lib/src/shift_register.dart @@ -38,11 +38,12 @@ class ShiftRegister extends Module { final String dataName; /// Creates a new shift register with specified [depth] which is only active - /// when [enable]d. If [reset] is provided, it will reset synchronously with - /// [clk] or aynchronously if [asyncReset] is true. The [reset] will reset all - /// stages to a default of `0` or to the provided [resetValue]. - /// If [resetValue] is a [List] the stages will reset to the corresponding - /// value in the list. + /// when [enable]d. + /// + /// If [reset] is provided, it will reset synchronously with [clk] or + /// aynchronously if [asyncReset] is true. The [reset] will reset all stages + /// to a default of `0` or to the provided [resetValue]. If [resetValue] is + /// a [List] the stages will reset to the corresponding value in the list. ShiftRegister( Logic dataIn, { required Logic clk, diff --git a/lib/src/summation/counter.dart b/lib/src/summation/counter.dart index 785f892a5..c57d1f0ef 100644 --- a/lib/src/summation/counter.dart +++ b/lib/src/summation/counter.dart @@ -58,6 +58,7 @@ class Counter extends SummationBase { required Logic reset, Logic? restart, dynamic resetValue = 0, + bool asyncReset = false, super.maxValue, super.minValue = 0, super.width, diff --git a/test/spi/spi_bfm_test.dart b/test/spi/spi_bfm_test.dart index 7cf6c5aca..b78f71d51 100644 --- a/test/spi/spi_bfm_test.dart +++ b/test/spi/spi_bfm_test.dart @@ -74,6 +74,18 @@ class SpiBfmTest extends Test { .add(SpiPacket(data: LogicValue.ofInt(0x1B, 8))); //0b0001 1011 = 27 })); + var packetReceived = false; + + await monitor.stream + .where((event) => + event.direction == SpiDirection.sub && event.data.toInt() == 0x1B) + .first + .then((_) { + packetReceived = true; + }); + + expect(packetReceived, true); + obj.drop(); } } diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index 80ef41616..0e8cdd708 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -68,11 +68,6 @@ class SpiMainTest extends Test { await clk.waitCycles(1); reset.inject(true); starts.inject(false); - await clk.waitCycles(1); - reset.inject(false); - await clk.waitCycles(1); - reset.inject(true); - await clk.waitCycles(1); reset.inject(false); await clk.waitCycles(1); @@ -110,10 +105,6 @@ class SpiSubTest extends Test { sub = SpiSub(intf: intf, busIn: busIn, reset: reset); - sub.busOut.changed.listen((_) { - logger.info('BusOut changed: ${sub.busOut.value}'); - }); - Directory(outFolder).createSync(recursive: true); final tracker = @@ -264,8 +255,7 @@ void main() { } Future sendSubPacket(SpiMainTest test, LogicValue data) async { - test.sub.sequencer.add(SpiPacket(data: data.reversed)); - // TODO: fix reversed on subdriver + test.sub.sequencer.add(SpiPacket(data: data)); await sendMainData(test, 0x00); } @@ -311,7 +301,7 @@ void main() { group('sub gasket tests', () { Future runSubTest(SpiSubTest spiSubTest, - {bool dumpWaves = true}) async { + {bool dumpWaves = false}) async { Simulator.setMaxSimTime(3000); final mod = SpiTop(spiSubTest.intf, spiSubTest); if (dumpWaves) { @@ -443,7 +433,7 @@ void main() { group('pair of gaskets tests', () { Future runPairTest(SpiPairTest spiPairTest, - {bool dumpWaves = true}) async { + {bool dumpWaves = false}) async { Simulator.setMaxSimTime(3000); final mod = SpiTop(spiPairTest.intf, null); if (dumpWaves) { diff --git a/test/spi/spi_test.dart b/test/spi/spi_test.dart index ee6475a9c..5e8d589f8 100644 --- a/test/spi/spi_test.dart +++ b/test/spi/spi_test.dart @@ -42,6 +42,7 @@ void main() { test('spi_test', () async { final mod = SpiTopIntf(); await mod.build(); - //print(mod.generateSynth()); + final genSV = mod.generateSynth(); + expect(genSV, contains('input logic MOSI')); }); } From 026f2bad856267948c4ec6d1fa86d1161c82d1dd Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Thu, 23 Jan 2025 09:42:42 -0800 Subject: [PATCH 23/31] shift reg asyncreset updated from sequential --- lib/src/shift_register.dart | 3 ++- test/spi/spi_gaskets_test.dart | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/src/shift_register.dart b/lib/src/shift_register.dart index 30b505a33..63b1f9520 100644 --- a/lib/src/shift_register.dart +++ b/lib/src/shift_register.dart @@ -111,9 +111,10 @@ class ShiftRegister extends Module { } Sequential.multi( - [clk, if (asyncReset && reset != null) reset], + [clk], reset: reset, resetValues: resetValues, + asyncReset: asyncReset, conds, ); diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index 0e8cdd708..141532065 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -8,6 +8,7 @@ // Author: Roberto Torres import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:rohd/rohd.dart'; @@ -53,6 +54,14 @@ class SpiMainTest extends Test { Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); + const numTransfers = 4; + final jsonStr = + File('$outFolder/spiTracker.tracker.json').readAsStringSync(); + final jsonContents = json.decode(jsonStr); + // ignore: avoid_dynamic_calls + expect(jsonContents['records'].length, 2 * numTransfers); + + Directory(outFolder).deleteSync(recursive: true); }); monitor.stream.listen(tracker.record); @@ -295,6 +304,11 @@ void main() { expect(test.main.busOut.value.toInt(), 0x56); await test.clk.waitCycles(4); + + await sendSubPacket(test, LogicValue.ofInt(0xAB, 8)); + + expect(test.main.busOut.value.toInt(), 0xAB); + await test.clk.waitCycles(4); }, 'testMainB')); }); }); From cf25fc2f1659f8561e859ba2c8c27796a39cf8ac Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Thu, 23 Jan 2025 10:28:17 -0800 Subject: [PATCH 24/31] added asyncReset flag to counter --- lib/src/gaskets/spi/spi_main.dart | 1 + lib/src/summation/counter.dart | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/src/gaskets/spi/spi_main.dart b/lib/src/gaskets/spi/spi_main.dart index 5878ff2a8..64870351c 100644 --- a/lib/src/gaskets/spi/spi_main.dart +++ b/lib/src/gaskets/spi/spi_main.dart @@ -55,6 +55,7 @@ class SpiMain extends Module { clk: ~clk, enable: start | isRunning, reset: reset, + asyncReset: true, minValue: 1, maxValue: busIn.width); diff --git a/lib/src/summation/counter.dart b/lib/src/summation/counter.dart index c57d1f0ef..991137334 100644 --- a/lib/src/summation/counter.dart +++ b/lib/src/summation/counter.dart @@ -27,6 +27,10 @@ class Counter extends SummationBase { @protected late final Logic reset; + /// Whether the reset is asynchronous. + @protected + late final bool asyncReset; + /// The restart signal. @protected late final Logic? restart; @@ -58,7 +62,7 @@ class Counter extends SummationBase { required Logic reset, Logic? restart, dynamic resetValue = 0, - bool asyncReset = false, + this.asyncReset = false, super.maxValue, super.minValue = 0, super.width, @@ -67,7 +71,6 @@ class Counter extends SummationBase { }) : super(initialValue: resetValue) { this.clk = addInput('clk', clk); this.reset = addInput('reset', reset); - if (restart != null) { this.restart = addInput('restart', restart); } else { @@ -96,8 +99,10 @@ class Counter extends SummationBase { buildFlops(); // need to flop these since value is flopped - overflowed <= flop(clk, summer.overflowed, reset: reset); - underflowed <= flop(clk, summer.underflowed, reset: reset); + overflowed <= + flop(clk, summer.overflowed, reset: reset, asyncReset: asyncReset); + underflowed <= + flop(clk, summer.underflowed, reset: reset, asyncReset: asyncReset); equalsMax <= count.eq(maxValueLogic); equalsMin <= count.eq(minValueLogic); @@ -112,6 +117,7 @@ class Counter extends SummationBase { summer.sum, reset: reset, resetValue: initialValueLogic, + asyncReset: asyncReset, ); } @@ -129,6 +135,7 @@ class Counter extends SummationBase { Logic? restart, bool saturates = false, bool increments = true, + bool asyncReset = false, int resetValue = 0, String name = 'counter', }) : this([ @@ -142,6 +149,7 @@ class Counter extends SummationBase { clk: clk, reset: reset, resetValue: resetValue, + asyncReset: asyncReset, restart: restart, maxValue: maxValue, minValue: minValue, @@ -166,6 +174,7 @@ class Counter extends SummationBase { Logic? enable, int? width, bool saturates = false, + bool asyncReset = false, String name = 'counter', }) => Counter( @@ -177,6 +186,7 @@ class Counter extends SummationBase { clk: clk, reset: reset, resetValue: resetValue, + asyncReset: asyncReset, maxValue: maxValue, minValue: minValue, width: width, From 76c6d0f288f4f0ba66b2a200394687934cd27c0e Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Thu, 23 Jan 2025 15:09:00 -0800 Subject: [PATCH 25/31] spi sub done signal added --- lib/src/gaskets/spi/spi_main.dart | 1 + lib/src/gaskets/spi/spi_sub.dart | 26 ++++++++++++++++++++++---- lib/src/summation/counter.dart | 5 ++--- test/spi/spi_gaskets_test.dart | 8 ++++++-- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/lib/src/gaskets/spi/spi_main.dart b/lib/src/gaskets/spi/spi_main.dart index 64870351c..4bf2a4d23 100644 --- a/lib/src/gaskets/spi/spi_main.dart +++ b/lib/src/gaskets/spi/spi_main.dart @@ -51,6 +51,7 @@ class SpiMain extends Module { final isRunning = Logic(name: 'isRunning'); + // Counter to track of the number of bits shifted out. final count = Counter.simple( clk: ~clk, enable: start | isRunning, diff --git a/lib/src/gaskets/spi/spi_sub.dart b/lib/src/gaskets/spi/spi_sub.dart index 5c1bd1627..d590e6dba 100644 --- a/lib/src/gaskets/spi/spi_sub.dart +++ b/lib/src/gaskets/spi/spi_sub.dart @@ -15,15 +15,21 @@ class SpiSub extends Module { /// Output bus from Sub. Logic get busOut => output('busOut'); + /// Done signal from Sub. + Logic get done => output('done'); + /// Creates a SPI Sub component that interfaces with [SpiInterface]. /// /// The SPI Sub component will enable via chip select from [SpiInterface.csb]. /// Clock signal will be received on [SpiInterface.sclk], data will shift in /// from [SpiInterface.mosi], and shift data out from [SpiInterface.miso]. - /// Data to shift out is provided from [busIn]. Data shifted in from - /// [SpiInterface.mosi] will be available on [busOut]. After data is available - /// on [busIn], pulsing [reset] will load the data, and a bit of data will be - /// transmitted per clock pulse. + /// Data shifted in from [SpiInterface.mosi] will be available on [busOut]. + /// + /// If [busIn] and [reset] are provided, data to shift out will be loaded from + /// [busIn]. After data is available on [busIn], pulsing [reset] will load the + /// data asynchronously, and a bit of data will be transmitted per pulse of + /// [SpiInterface.sclk]. After all data is shifted out, an optional [done] + /// signal will indicate completion. SpiSub( {required SpiInterface intf, Logic? busIn, @@ -48,6 +54,18 @@ class SpiSub extends Module { addOutput('done'); + // Counter to track of the number of bits shifted out. + final count = Counter.simple( + clk: intf.sclk, + enable: ~intf.csb, + reset: reset ?? Const(0, width: 1), + asyncReset: true, + minValue: 1, + maxValue: intf.dataLength); + + // Done signal will be high when the counter is at the max value. + done <= count.equalsMax; + // Shift Register in from MOSI. // NOTE: Reset values are set to busIn values. final shiftReg = ShiftRegister( diff --git a/lib/src/summation/counter.dart b/lib/src/summation/counter.dart index 991137334..b7480e649 100644 --- a/lib/src/summation/counter.dart +++ b/lib/src/summation/counter.dart @@ -27,9 +27,8 @@ class Counter extends SummationBase { @protected late final Logic reset; - /// Whether the reset is asynchronous. - @protected - late final bool asyncReset; + /// Whether the [reset] is asynchronous. + final bool asyncReset; /// The restart signal. @protected diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index 141532065..0b33197c5 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -121,6 +121,7 @@ class SpiSubTest extends Test { Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); + Directory(outFolder).deleteSync(recursive: true); }); monitor.stream.listen(tracker.record); @@ -197,6 +198,7 @@ class SpiPairTest extends Test { Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); + Directory(outFolder).deleteSync(recursive: true); }); monitor.stream.listen(tracker.record); @@ -272,6 +274,7 @@ void main() { await runMainTest(SpiMainTest((test) async { await sendSubPacket(test, LogicValue.ofInt(0x72, 8)); expect(test.main.busOut.value.toInt(), 0x72); + expect(test.main.done.value.toBool(), true); // await test.clk.nextNegedge; await sendSubPacket(test, LogicValue.ofInt(0xCD, 8)); @@ -293,8 +296,9 @@ void main() { await sendSubPacket(test, LogicValue.ofInt(0x72, 8)); expect(test.main.busOut.value.toInt(), 0x72); - await test.clk.waitCycles(3); + expect(test.main.done.value.toBool(), true); + await test.clk.waitCycles(3); await sendSubPacket(test, LogicValue.ofInt(0xCD, 8)); // 1100 1101 expect(test.main.busOut.value.toInt(), 0xCD); @@ -453,7 +457,6 @@ void main() { if (dumpWaves) { await mod.build(); WaveDumper(mod, outputPath: '${spiPairTest.outFolder}/waves.vcd'); - // print(mod.generateSynth()); } await spiPairTest.start(); } @@ -467,6 +470,7 @@ void main() { test.starts.inject(true); await test.clk.waitCycles(1); test.starts.inject(false); + expect(test.sub.done.value.toBool(), false); await test.clk.waitCycles(7); } From b499664845088abc1b7ae03098e6693fbb6b9df8 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Fri, 24 Jan 2025 08:49:23 -0800 Subject: [PATCH 26/31] counter asyncReset tests added --- test/summation/counter_test.dart | 82 ++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/test/summation/counter_test.dart b/test/summation/counter_test.dart index 6fcd565a9..c756888cc 100644 --- a/test/summation/counter_test.dart +++ b/test/summation/counter_test.dart @@ -272,6 +272,88 @@ void main() { await Simulator.endSimulation(); }); + test('async reset, clock tied off', () async { + final clk = SimpleClockGenerator(10).clk; + final reset = Logic(); + final restart = Logic(); + + final counter = Counter( + [ + SumInterface(fixedAmount: 4), + SumInterface(fixedAmount: 2, increments: false), + ], + clk: Const(0), + reset: reset, + restart: restart, + asyncReset: true, + resetValue: 4, + maxValue: 10, + minValue: 1, + ); + + await counter.build(); + + Simulator.setMaxSimTime(1000); + unawaited(Simulator.run()); + WaveDumper(counter); + // initializing/resetting with no clock + await clk.waitCycles(2); + reset.inject(0); + restart.inject(0); + await clk.nextNegedge; + reset.inject(1); + await clk.nextPosedge; + expect(counter.count.value.toInt(), 4); + + await clk.waitCycles(2); + await Simulator.endSimulation(); + }); + + test('async reset with clock', () async { + final clk = SimpleClockGenerator(10).clk; + final reset = Logic(); + final restart = Logic(); + + final counter = Counter( + [ + SumInterface(fixedAmount: 4), + SumInterface(fixedAmount: 2, increments: false), + ], + clk: clk, + reset: reset, + restart: restart, + asyncReset: true, + resetValue: 2, + maxValue: 20, + minValue: 1, + ); + + await counter.build(); + + Simulator.setMaxSimTime(1000); + unawaited(Simulator.run()); + + // initial reset flow + reset.inject(0); + restart.inject(0); + await clk.nextNegedge; + reset.inject(1); + await clk.nextPosedge; + reset.inject(0); + + // check counter counts + await clk.waitCycles(4); + expect(counter.count.value.toInt(), 10); + + // reset + reset.inject(1); + await clk.nextChanged; + expect(counter.count.previousValue!.toInt(), 2); + await clk.waitCycles(2); + + await Simulator.endSimulation(); + }); + group('random counter', () { const numRandCounters = 20; const restartProbability = 0.05; From 827535812e40f59453452f8f5c0a222f27947e68 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Fri, 24 Jan 2025 10:02:23 -0800 Subject: [PATCH 27/31] more cleanup --- lib/src/models/spi_bfm/spi_main_driver.dart | 10 +++++----- lib/src/models/spi_bfm/spi_monitor.dart | 2 +- lib/src/models/spi_bfm/spi_sub_driver.dart | 2 +- test/spi/spi_gaskets_test.dart | 6 ------ 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/src/models/spi_bfm/spi_main_driver.dart b/lib/src/models/spi_bfm/spi_main_driver.dart index 65bae935a..92004a994 100644 --- a/lib/src/models/spi_bfm/spi_main_driver.dart +++ b/lib/src/models/spi_bfm/spi_main_driver.dart @@ -29,8 +29,8 @@ class SpiMainDriver extends PendingClockedDriver { super.dropDelayCycles = 30, String name = 'spiMainDriver', }) : super(name, parent) { - intf.sclk <= ~clk & clkenable; - clkenable.inject(0); + intf.sclk <= ~clk & _clkenable; + _clkenable.inject(0); } @override @@ -49,7 +49,7 @@ class SpiMainDriver extends PendingClockedDriver { await clk.nextNegedge; Simulator.injectAction(() { intf.csb.put(1); - clkenable.inject(0); + _clkenable.inject(0); intf.mosi.put(0); }); } @@ -57,7 +57,7 @@ class SpiMainDriver extends PendingClockedDriver { } /// Clock enable signal. - Logic clkenable = Logic(name: 'clkenable'); + final _clkenable = Logic(name: 'clkenable'); /// Drives a packet onto the interface. Future _drivePacket(SpiPacket packet) async { @@ -67,7 +67,7 @@ class SpiMainDriver extends PendingClockedDriver { for (var i = 1; i <= packet.data.width; i++) { intf.mosi.inject(packet.data[-i]); await clk.nextNegedge; - clkenable.inject(1); + _clkenable.inject(1); // Wait for the next clock cycle await clk.nextPosedge; diff --git a/lib/src/models/spi_bfm/spi_monitor.dart b/lib/src/models/spi_bfm/spi_monitor.dart index 9d35316ca..61aab62d1 100644 --- a/lib/src/models/spi_bfm/spi_monitor.dart +++ b/lib/src/models/spi_bfm/spi_monitor.dart @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Intel Corporation +// Copyright (C) 2024-2025 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // spi_monitor.dart diff --git a/lib/src/models/spi_bfm/spi_sub_driver.dart b/lib/src/models/spi_bfm/spi_sub_driver.dart index 463ee5271..2e03e6f3b 100644 --- a/lib/src/models/spi_bfm/spi_sub_driver.dart +++ b/lib/src/models/spi_bfm/spi_sub_driver.dart @@ -43,7 +43,7 @@ class SpiSubDriver extends PendingDriver { /// The pending packet. SpiPacket? _packet; - /// + /// The index of the data. int? _dataIndex; // Function handles the packet. diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index 0b33197c5..ecd1bbfe3 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -423,14 +423,8 @@ void main() { expect(test.sub.busOut.value.toInt(), 0x72); // inject bus in on neg edge of same cycle - // will result in 1 clk cycle delay? assuming you had to read on pos and - // inject on neg edge await test.clk.nextNegedge; - - // td: when busin injected its sent out MSB, should be LSB test.busIn.inject(0x19); - - //td: but here running into the extra clk cycle issue in main driver? test.main.sequencer.add(SpiPacket(data: LogicValue.ofInt(0x00, 8))); // trigger reset From acaeddc83e11315cae61d654b23211f4b47c6d61 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Fri, 24 Jan 2025 16:05:35 -0800 Subject: [PATCH 28/31] spi checker added --- lib/src/models/spi_bfm/spi_bfm.dart | 1 + lib/src/models/spi_bfm/spi_checker.dart | 50 +++++++++++++++++++++++++ test/spi/spi_gaskets_test.dart | 13 ++++--- 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 lib/src/models/spi_bfm/spi_checker.dart diff --git a/lib/src/models/spi_bfm/spi_bfm.dart b/lib/src/models/spi_bfm/spi_bfm.dart index 82a48b512..8c4724f3a 100644 --- a/lib/src/models/spi_bfm/spi_bfm.dart +++ b/lib/src/models/spi_bfm/spi_bfm.dart @@ -7,6 +7,7 @@ // 2024 September 23 // Author: Roberto Torres +export 'spi_checker.dart'; export 'spi_main_agent.dart'; export 'spi_main_driver.dart'; export 'spi_monitor.dart'; diff --git a/lib/src/models/spi_bfm/spi_checker.dart b/lib/src/models/spi_bfm/spi_checker.dart new file mode 100644 index 000000000..306a7e026 --- /dev/null +++ b/lib/src/models/spi_bfm/spi_checker.dart @@ -0,0 +1,50 @@ +// Copyright (C) 2025 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// spi_checker.dart +// Implementation of SPI Checker component. +// +// 2025 January 22 +// Author: Roberto Torres + +import 'dart:async'; +import 'package:rohd/rohd.dart'; +import 'package:rohd_hcl/rohd_hcl.dart'; +import 'package:rohd_vf/rohd_vf.dart'; + +/// Checker component for Serial Peripheral Interface (SPI). +class SpiChecker extends Component { + /// Error signal from Checker. + final SpiInterface intf; + + /// Creates a SPI Checker component that interfaces with [SpiInterface]. + SpiChecker( + this.intf, { + required Component parent, + String name = 'spiChecker', + }) : super(name, parent); + + @override + Future run(Phase phase) async { + unawaited(super.run(phase)); + + LogicValue? mosiVal; + LogicValue? misoVal; + + // save the value of mosi and miso on posedge + intf.sclk.posedge.listen((event) { + mosiVal = intf.mosi.value; + misoVal = intf.miso.value; + }); + + // checking prev value at negedge + intf.sclk.negedge.listen((event) { + if (misoVal != null && misoVal != intf.miso.previousValue) { + logger.severe('Data on MISO is changing on posedge of sclk'); + } + if (mosiVal != null && mosiVal != intf.mosi.previousValue) { + logger.severe('Data on MOSI is changing on posedge of sclk'); + } + }); + } +} diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index ecd1bbfe3..0d6134bd2 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -42,6 +42,8 @@ class SpiMainTest extends Test { final tracker = SpiTracker(intf: intf, dumpTable: false, outputFolder: outFolder); + SpiChecker(intf, parent: this); + clk = SimpleClockGenerator(10).clk; // initialize the bus with 00 @@ -119,6 +121,8 @@ class SpiSubTest extends Test { final tracker = SpiTracker(intf: intf, dumpTable: false, outputFolder: outFolder); + SpiChecker(intf, parent: this); + Simulator.registerEndOfSimulationAction(() async { await tracker.terminate(); Directory(outFolder).deleteSync(recursive: true); @@ -181,6 +185,8 @@ class SpiPairTest extends Test { final tracker = SpiTracker(intf: intf, dumpTable: false, outputFolder: outFolder); + SpiChecker(intf, parent: this); + clk = SimpleClockGenerator(10).clk; // initialize main @@ -276,15 +282,12 @@ void main() { expect(test.main.busOut.value.toInt(), 0x72); expect(test.main.done.value.toBool(), true); - // await test.clk.nextNegedge; await sendSubPacket(test, LogicValue.ofInt(0xCD, 8)); expect(test.main.busOut.value.toInt(), 0xCD); - // await test.clk.nextNegedge; await sendSubPacket(test, LogicValue.ofInt(0x56, 8)); expect(test.main.busOut.value.toInt(), 0x56); - // await test.clk.nextNegedge; await sendSubPacket(test, LogicValue.ofInt(0xE2, 8)); expect(test.main.busOut.value.toInt(), 0xE2); await test.clk.waitCycles(4); @@ -366,10 +369,10 @@ void main() { .add(SpiPacket(data: LogicValue.ofInt(0xCD, 8))); // 1100 1101 await test.clk.waitCycles(8); - + expect(test.sub.done.value.toBool(), false); await test.clk.nextPosedge; expect(test.sub.busOut.value.toInt(), 0xCD); - + expect(test.sub.done.value.toBool(), true); //gap await test.clk.waitCycles(7); From aa8e0aad4107f998fc935f941f31ca15bd728399 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Mon, 27 Jan 2025 14:07:25 -0800 Subject: [PATCH 29/31] final documentation add, small changes to gasket testing --- doc/README.md | 4 +- doc/components/spi_bfm.md | 17 ++++ doc/components/spi_gaskets.md | 37 +++++++++ doc/components/standard_interfaces.md | 4 + lib/rohd_hcl.dart | 2 +- .../spi/{spi.dart => spi_gaskets.dart} | 0 lib/src/models/spi_bfm/spi_checker.dart | 4 +- test/spi/spi_gaskets_test.dart | 82 ++++++++++++++----- 8 files changed, 125 insertions(+), 25 deletions(-) create mode 100644 doc/components/spi_bfm.md create mode 100644 doc/components/spi_gaskets.md rename lib/src/gaskets/spi/{spi.dart => spi_gaskets.dart} (100%) diff --git a/doc/README.md b/doc/README.md index 8dcf21fd7..3d63a3f8d 100644 --- a/doc/README.md +++ b/doc/README.md @@ -109,14 +109,14 @@ Some in-development items will have opened issues, as well. Feel free to create - PCIe - UCIe - JTAG - - SPI + - [SPI](./components/standard_interfaces.md#spi) - UART - DDR - HBM - Models - [APB](./components/apb_bfm.md) - [Ready/Valid](./components/ready_valid_bfm.md) - - SPI + - [SPI](./components/spi_bfm.md) - CXL ---------------- diff --git a/doc/components/spi_bfm.md b/doc/components/spi_bfm.md new file mode 100644 index 000000000..672d8673c --- /dev/null +++ b/doc/components/spi_bfm.md @@ -0,0 +1,17 @@ +# SPI BFM + +The SPI BFM is a collection of [ROHD-VF](https://github.com/intel/rohd-vf) components and objects that are helpful for validating hardware that contains an SPI interface. It includes all the basic SPI interface features for sending and responding to data between a main-sub connection. + +The main two components are the `SpiMainAgent` and the `SpiSubAgent`, which behave like a "Main" and "Sub" as commonly seen in SPI implementations. Both have a standard `Sequencer` that accepts `SpiPacket`s to be driven out to each other. Both also have a corresponding `Driver` as `SpiMainDriver` and `SpiSubDriver`, respectively, that handle the driving of data onto the `SpiInterface`. + +An `SpiMonitor` is also included, which implements the standard `Monitor` and provides a stream of `SpiPacket`s monitored on positive edges of the clock. The `SpiTracker` can be used to log all items detected by the monitor by implementing the standard `Tracker` API (log file or JSON both supported). + +Finally, a `SpiChecker` monitors an `SpiInterface` for a subset of the rules commonly used in SPI implementations. Errors are flagged using the `severe` log messages, as is standard for errors in ROHD-VF. + +The unit tests in `spi_bfm_test.dart`, which have a main and sub communicating with each other, are a good example for setting up the SPI BFM. The unit test in `spi_gaskets_test` also have good example of the SPI BFM interacting with their corresponding hardware components. + +## Unsupported features + +The following features are currently not supported by or have no utilities within the BFM: + +- **CPOL/CPHA**: different clock polarity and clock phase are not considered, the BFM is implemented with a SPI Mode = 0 (CPOL/CPHA = 0). diff --git a/doc/components/spi_gaskets.md b/doc/components/spi_gaskets.md new file mode 100644 index 000000000..c2b5848f3 --- /dev/null +++ b/doc/components/spi_gaskets.md @@ -0,0 +1,37 @@ +# SPI Gaskets + +ROHD-HCL implements a `SpiMain` and `SpiSub` set of components that enable communication via a Serial Peripheral Interface. + +## SpiMain + +Interacts as the provider on the `SpiInterface`. + +The inputs to the `SpiMain` component are: + +* `clk` => clock for synchronous logic and driving `SpiInterface.sclk` +* `reset` => asynchronous reset for component and to reset `busIn` values +* `start` => to initiate a data transfer +* `busIn` => to load data to transmit + +The outputs to the `SpiSub` component are: + +* `busOut` => to output data received +* `done` => signals completion of a data transfer + +When data is available on `busIn`, pulsing `reset` will load the data into the internal shift register. Pulsing `start` will make `SpiInterface.csb` active and begin driving a clock signal on `SpiInterface.sclk`. On every clock pulse data will shift out onto `SpiInterface.mosi` and shift in from `SpiInterface.miso`. Data shifted in will be avaible on `busOut`. The `done` signal will indicate when transmissions are complete. + +## SpiSub + +Interacts as the consumer on the `SpiInterface`. + +The inputs to the `SpiSub` component are: + +* `reset` => optional input, asynchronous reset for component and to reset `busIn` values +* `busIn` => optional input, to load data to transmit + +The outputs to the `SpiSub` component are: + +* `busOut` => to output data received +* `done` => signals completion of a data transfer + +When data is available on `busIn`, pulsing `reset` will load the data into the internal shift register. When `SpiInterface.csb` is active and clock signal is present on `SpiInterface.sclk`, data will shift out onto `SpiInterface.miso` and shift in from `SpiInterface.mosi`. Data shifted in will be avaible on `busOut`. The `done` signal will indicate when transmissions are complete. diff --git a/doc/components/standard_interfaces.md b/doc/components/standard_interfaces.md index 8bb3ddb49..8768c8d11 100644 --- a/doc/components/standard_interfaces.md +++ b/doc/components/standard_interfaces.md @@ -5,3 +5,7 @@ ROHD-HCL provides a set of standard interfaces using ROHD `Interface`s. This ma ## APB The [ABP Interface](https://developer.arm.com/documentation/ihi0024/latest/) is a standard AMBA interface. ROHD HCL has a configurable version of the APB interface called [`ApbInterface`](https://intel.github.io/rohd-hcl/rohd_hcl/ApbInterface-class.html). + +## SPI + +The Serial Peripheral Interface (SPI) is a common serial communicaton interface. ROHD HCL has a configurable version of the SPI interface called [`SpiInterface`](https://intel.github.io/rohd-hcl/rohd_hcl/SpiInterface-class.html). diff --git a/lib/rohd_hcl.dart b/lib/rohd_hcl.dart index e35a466b8..f6fb5734b 100644 --- a/lib/rohd_hcl.dart +++ b/lib/rohd_hcl.dart @@ -14,7 +14,7 @@ export 'src/exceptions.dart'; export 'src/extrema.dart'; export 'src/fifo.dart'; export 'src/find.dart'; -export 'src/gaskets/spi/spi.dart'; +export 'src/gaskets/spi/spi_gaskets.dart'; export 'src/interfaces/interfaces.dart'; export 'src/memory/memories.dart'; export 'src/models/models.dart'; diff --git a/lib/src/gaskets/spi/spi.dart b/lib/src/gaskets/spi/spi_gaskets.dart similarity index 100% rename from lib/src/gaskets/spi/spi.dart rename to lib/src/gaskets/spi/spi_gaskets.dart diff --git a/lib/src/models/spi_bfm/spi_checker.dart b/lib/src/models/spi_bfm/spi_checker.dart index 306a7e026..eeefd4512 100644 --- a/lib/src/models/spi_bfm/spi_checker.dart +++ b/lib/src/models/spi_bfm/spi_checker.dart @@ -14,7 +14,7 @@ import 'package:rohd_vf/rohd_vf.dart'; /// Checker component for Serial Peripheral Interface (SPI). class SpiChecker extends Component { - /// Error signal from Checker. + /// Interface to check. final SpiInterface intf; /// Creates a SPI Checker component that interfaces with [SpiInterface]. @@ -31,7 +31,7 @@ class SpiChecker extends Component { LogicValue? mosiVal; LogicValue? misoVal; - // save the value of mosi and miso on posedge + // Save the value of mosi and miso on posedge intf.sclk.posedge.listen((event) { mosiVal = intf.mosi.value; misoVal = intf.miso.value; diff --git a/test/spi/spi_gaskets_test.dart b/test/spi/spi_gaskets_test.dart index 0d6134bd2..f5b8d1149 100644 --- a/test/spi/spi_gaskets_test.dart +++ b/test/spi/spi_gaskets_test.dart @@ -260,61 +260,103 @@ void main() { await spiMainTest.start(); } - Future sendMainData(SpiMainTest test, int data) async { - test.busInMain.inject(LogicValue.ofInt(data, test.intf.dataLength)); - test.reset.inject(true); - await test.clk.nextPosedge; - test.reset.inject(false); - test.starts.inject(true); - await test.clk.waitCycles(1); - test.starts.inject(false); - await test.clk.waitCycles(7); - } - - Future sendSubPacket(SpiMainTest test, LogicValue data) async { - test.sub.sequencer.add(SpiPacket(data: data)); - await sendMainData(test, 0x00); - } - test('simple transfers no gap', () async { await runMainTest(SpiMainTest((test) async { + Future sendMainData(SpiMainTest test, int data) async { + test.busInMain.inject(LogicValue.ofInt(data, test.intf.dataLength)); + test.reset.inject(true); + await test.clk.nextNegedge; + test.reset.inject(false); + test.starts.inject(true); + await test.clk.waitCycles(1); + test.starts.inject(false); + await test.clk.waitCycles(6); + await test.clk.nextPosedge; + } + + Future sendSubPacket(SpiMainTest test, LogicValue data) async { + test.sub.sequencer.add(SpiPacket(data: data)); + await sendMainData(test, 0x00); + } + + var clkCount = 0; + test.clk.negedge.listen((event) { + clkCount++; + }); + final txPeriod = test.intf.dataLength; + await sendSubPacket(test, LogicValue.ofInt(0x72, 8)); expect(test.main.busOut.value.toInt(), 0x72); - expect(test.main.done.value.toBool(), true); + expect(clkCount, txPeriod); await sendSubPacket(test, LogicValue.ofInt(0xCD, 8)); expect(test.main.busOut.value.toInt(), 0xCD); + expect(clkCount, 2 * txPeriod); await sendSubPacket(test, LogicValue.ofInt(0x56, 8)); expect(test.main.busOut.value.toInt(), 0x56); + expect(clkCount, 3 * txPeriod); await sendSubPacket(test, LogicValue.ofInt(0xE2, 8)); expect(test.main.busOut.value.toInt(), 0xE2); + expect(clkCount, 4 * txPeriod); + await test.clk.waitCycles(4); }, 'testMainA')); }); test('simple transfers with gaps', () async { await runMainTest(SpiMainTest((test) async { + Future sendMainData(SpiMainTest test, int data) async { + test.busInMain.inject(LogicValue.ofInt(data, test.intf.dataLength)); + test.reset.inject(true); + await test.clk.nextPosedge; + test.reset.inject(false); + test.starts.inject(true); + await test.clk.waitCycles(1); + test.starts.inject(false); + await test.clk.waitCycles(7); + } + + Future sendSubPacket(SpiMainTest test, LogicValue data) async { + test.sub.sequencer.add(SpiPacket(data: data)); + await sendMainData(test, 0x00); + } + + var clkCount = 0; + test.clk.negedge.listen((event) { + clkCount++; + }); + + final clkPeriod = test.intf.dataLength + 1; await sendSubPacket(test, LogicValue.ofInt(0x72, 8)); expect(test.main.busOut.value.toInt(), 0x72); expect(test.main.done.value.toBool(), true); + expect(clkCount, clkPeriod); await test.clk.waitCycles(3); - await sendSubPacket(test, LogicValue.ofInt(0xCD, 8)); // 1100 1101 + expect(clkCount, clkPeriod + 3); + await sendSubPacket(test, LogicValue.ofInt(0xCD, 8)); // 1100 1101 expect(test.main.busOut.value.toInt(), 0xCD); + expect(test.main.done.value.toBool(), true); + expect(clkCount, (2 * clkPeriod) + 3); + await test.clk.waitCycles(4); await sendSubPacket(test, LogicValue.ofInt(0x56, 8)); - expect(test.main.busOut.value.toInt(), 0x56); + expect(test.main.done.value.toBool(), true); + expect(clkCount, (3 * clkPeriod) + 7); + await test.clk.waitCycles(4); await sendSubPacket(test, LogicValue.ofInt(0xAB, 8)); - expect(test.main.busOut.value.toInt(), 0xAB); + expect(test.main.done.value.toBool(), true); + expect(clkCount, (4 * clkPeriod) + 11); + await test.clk.waitCycles(4); }, 'testMainB')); }); From ecd7c09fcb8fde399b8b1cca337971e1d11a8655 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Mon, 27 Jan 2025 14:14:29 -0800 Subject: [PATCH 30/31] public doc change --- lib/src/models/spi_bfm/spi_packet.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/src/models/spi_bfm/spi_packet.dart b/lib/src/models/spi_bfm/spi_packet.dart index 7db441c5c..18d5a7b29 100644 --- a/lib/src/models/spi_bfm/spi_packet.dart +++ b/lib/src/models/spi_bfm/spi_packet.dart @@ -14,7 +14,13 @@ import 'package:rohd_hcl/rohd_hcl.dart'; import 'package:rohd_vf/rohd_vf.dart'; /// Direction of the packet. -enum SpiDirection { main, sub } +enum SpiDirection { + /// Main direction. + main, + + /// Sub direction. + sub +} /// A packet for the [SpiInterface]. class SpiPacket extends SequenceItem implements Trackable { From 29abd76540bf96cda99ed254323f6273597d0709 Mon Sep 17 00:00:00 2001 From: Roberto Torres Date: Mon, 27 Jan 2025 14:24:41 -0800 Subject: [PATCH 31/31] removed todos --- lib/src/interfaces/spi.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/interfaces/spi.dart b/lib/src/interfaces/spi.dart index cf19c25a2..bc29c04ee 100644 --- a/lib/src/interfaces/spi.dart +++ b/lib/src/interfaces/spi.dart @@ -16,7 +16,6 @@ class SpiInterface extends PairInterface { /// Serial clock (SCLK). Clock signal from main to sub(s). Logic get sclk => port('SCLK'); - // TODO(rt): add CPOL/CPHA support /// Main Out Sub In (MOSI). Serial data from main to sub(s). Logic get mosi => port('MOSI'); @@ -26,7 +25,6 @@ class SpiInterface extends PairInterface { /// Chip select (active low). Chip select signal from main to sub. Logic get csb => port('CSB'); - // TODO(rt): add multiple CSB support /// Creates a new [SpiInterface]. SpiInterface({this.dataLength = 1})